add partial application util

This commit is contained in:
Kacper Donat 2018-07-15 20:00:33 +02:00
parent 287f07dba6
commit fd3288a4ad
5 changed files with 148 additions and 0 deletions

View File

@ -0,0 +1,11 @@
<phpunit bootstrap="tests/bootstrap.php">
<testsuites>
<testsuite name="all">
<directory>tests</directory>
</testsuite>
</testsuites><filter>
<whitelist processUncoveredFilesFromWhitelist="true">
<directory suffix=".php">src</directory>
</whitelist>
</filter>
</phpunit>

View File

@ -0,0 +1,53 @@
<?php
/**
* Copyright 2018 Kacper Donat
*
* @author Kacper "Kadet" Donat <kacper@kadet.net>
*
* Full license available in separate LICENSE file
*/
namespace Kadet\Functional\Utils;
use const Kadet\Functional\_ as PLACEHOLDER;
class PartiallyAppliedFunction
{
protected $callable;
protected $arguments = [];
public function __construct(callable $callable, ...$args)
{
switch (true) {
case $callable instanceof PartiallyAppliedFunction:
$this->callable = $callable->callable;
$this->arguments = $callable->prepareArguments($args);
break;
default:
$this->callable = $callable;
$this->arguments = array_values($args);
}
}
public function __invoke(...$args)
{
$arguments = $this->prepareArguments($args);
return ($this->callable)(...$arguments);
}
private function prepareArguments($args)
{
$result = [];
foreach ($this->arguments as $argument) {
$result[] = $argument === PLACEHOLDER
? array_shift($args)
: $argument;
}
return array_merge(
array_values($result),
array_values($args)
);
}
}

17
src/Utils/index.php Normal file
View File

@ -0,0 +1,17 @@
<?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\PartiallyAppliedFunction;
function partial(callable $callable, ...$args)
{
return new PartiallyAppliedFunction($callable, ...$args);
}

View File

@ -7,6 +7,8 @@ use Kadet\Functional\Predicate\AnyOfPredicate;
use Kadet\Functional\Predicate\ClosurePredicate;
use Kadet\Functional\Predicate\ConstantPredicate;
require_once __DIR__.'/Utils/index.php';
/**
* @param $predicate
*
@ -57,3 +59,11 @@ function any(...$predicates)
$predicates = unpack($predicates);
return new AnyOfPredicate(...array_map(predicate::class, $predicates));
}
function symbol($name = null)
{
$id = random_bytes(64);
return sprintf("%s(%s)", $name ?: 'symbol', $id);
}
define(__NAMESPACE__.'\\_', symbol());

View File

@ -0,0 +1,57 @@
<?php
/**
* Copyright 2018 Kacper Donat
*
* @author Kacper "Kadet" Donat <kacper@kadet.net>
*
* Full license available in separate LICENSE file
*/
use \Kadet\Functional as f;
use const Kadet\Functional\_;
function subtract($a, $b) {
return $a - $b;
}
class PartialApplicationTest extends \PHPUnit\Framework\TestCase
{
public function testPartialApplicationOfOneArgument()
{
$applied = f\partial('subtract', 10);
$this->assertEquals(0, $applied(10));
$this->assertEquals(10, $applied(0));
$this->assertEquals(20, $applied(-10));
}
public function testPartialApplicationOfAllArguments()
{
$applied = f\partial('subtract', 10, 20);
$this->assertEquals(-10, $applied());
}
public function testPartialApplicationOfFewArguments()
{
$applied = f\partial('array_merge', ['a'], ['b'], ['c']);
$this->assertEquals(['a', 'b', 'c', 'd', 'e'], $applied(['d'], ['e']));
}
public function testPartialApplicationWithPlaceholders()
{
$applied = f\partial('array_merge', ['a'], _, ['c']);
$this->assertEquals(['a', 'd', 'c'], $applied(['d']));
}
public function testPartialApplicationOfAppliedFunction()
{
$applied = f\partial('array_merge', ['a'], _, ['c']);
$double = f\partial($applied, _, ['d']);
$this->assertEquals(['a', 'b', 'c', 'd', 'e'], $double(['b'], ['e']));
}
}