[Fun] Add pipe and after combinators (#57)

This commit is contained in:
Toon Verwerft 2020-09-28 15:57:00 +02:00 committed by GitHub
parent 25480b41fa
commit 40a4574e9d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 158 additions and 0 deletions

41
src/Psl/Fun/after.php Normal file
View File

@ -0,0 +1,41 @@
<?php
declare(strict_types=1);
namespace Psl\Fun;
/**
* Returns a function that calls the next function with the result of the first function.
*
* Example:
*
* $runExpression = Fun\after(
* fn($i) => $i + 1,
* fn(int $i) => $i * 5
* );
* => $runExpression(1) === 10;
* => $runExpression(2) === 15;
* => $runExpression(3) === 20;
*
* $greet = Fun\after(
* fn($value) => 'Hello' . $value,
* fn($value) => $value . '!'
* );
* => $greet('World') === 'Hello World!';
* => $greet('Jim') === 'Hello Jim!';
*
* @template I
* @template O
* @template R
*
* @psalm-param callable(I): O $first
* @psalm-param callable(O): R $next
*
* @psalm-return callable(I): R
*
* @psalm-pure
*/
function after(callable $first, callable $next): callable
{
return static fn ($input) => $next($first($input));
}

43
src/Psl/Fun/pipe.php Normal file
View File

@ -0,0 +1,43 @@
<?php
declare(strict_types=1);
namespace Psl\Fun;
use function Psl\Iter\reduce;
/**
* Performs left-to-right function composition.
* Example
* $pipe = Fun\pipe(
* fn($value) => 'Hello' . $value,
* fn($value) => $value . '!',
* fn($value) => '¡' . $value
* );
* => $greet('World') === '¡Hello World!';
* => $greet('Jim') === '¡Hello Jim!';
*
* @template T
*
* @psalm-param callable(T): T ...$stages
*
* @psalm-return callable(T): T
*
* @psalm-pure
*/
function pipe(callable ...$stages): callable
{
return static fn ($input) => reduce(
$stages,
/**
* @template IO
*
* @param IO $input
* @param callable(IO): IO $next
*
* @return IO
*/
static fn ($input, int $key, callable $next) => $next($input),
$input
);
}

View File

@ -95,6 +95,8 @@ final class Loader
'Psl\Arr\map', 'Psl\Arr\map',
'Psl\Arr\map_keys', 'Psl\Arr\map_keys',
'Psl\Arr\map_with_key', 'Psl\Arr\map_with_key',
'Psl\Fun\after',
'Psl\Fun\pipe',
'Psl\Asio\wrap', 'Psl\Asio\wrap',
'Psl\Internal\boolean', 'Psl\Internal\boolean',
'Psl\Internal\type', 'Psl\Internal\type',

View File

@ -0,0 +1,32 @@
<?php
declare(strict_types=1);
namespace Psl\Tests\Fun;
use PHPUnit\Framework\TestCase;
use Psl\Fun;
use Psl\Str;
final class AfterTest extends TestCase
{
public function testItCombinesAFunctionToExecuteAFunctionAfterAnotherFunction(): void
{
$x = Fun\after(
fn (string $x): string => $x . ' world',
fn (string $z): string => $z . '!!'
);
self::assertSame('Hello world!!', $x('Hello'));
}
public function testItCombinesAFunctionThatDealWithDifferentTypes(): void
{
$x = Fun\after(
fn (string $x): int => Str\length($x),
fn (int $z): string => $z . '!'
);
self::assertSame('5!', $x('Hello'));
}
}

View File

@ -0,0 +1,40 @@
<?php
declare(strict_types=1);
namespace Psl\Tests\Fun;
use PHPUnit\Framework\TestCase;
use Psl\Fun;
use Psl\Str;
final class PipeTest extends TestCase
{
public function testItCombinesMultipleFunctionToExecutesInOrder(): void
{
$x = Fun\pipe(
fn (string $x): string => $x . ' world',
fn (string $y): string => $y . '?',
fn (string $z): string => $z . '!',
);
self::assertSame('Hello world?!', $x('Hello'));
}
public function testItCombinesMultipleFunctionsThatDealWithDifferentTypes(): void
{
$x = Fun\pipe(
fn (string $x): int => Str\length($x),
fn (int $y): string => $y . '!'
);
self::assertSame('5!', $x('Hello'));
}
public function testItCanCreateAnEmptyCombination(): void
{
$x = Fun\pipe();
self::assertSame('Hello', $x('Hello'));
}
}