Functional Programming Toolbox¶
Functional Programming Toolbox (FPT for the friends) is a set of stateless helper classes to facilitate the use of functional programming concepts.
Documentation¶
The API will give you a pretty good idea of the existing methods and what you can do with it.
I’m doing my best to keep the documentation up to date, if you found something odd, please let me know in the issue queue.
Code quality, tests and benchmarks¶
Every time changes are introduced into the library, Github run the tests.
The library has tests written with PHPSpec. Feel free to
check them out in the spec
directory. Run composer phpspec
to
trigger the tests.
Before each commit some inspections are executed with
GrumPHP, run composer grumphp
to check manually.
The quality of the tests is tested with Infection a PHP
Mutation testing framework, run composer infection
to try it.
Static analysers are also controlling the code. PHPStan and PSalm are enabled to their maximum level.
Contributing¶
Feel free to contribute by sending Github pull requests. I’m quite reactive :-)
If you can’t contribute to the code, you can also sponsor me on Github or Paypal.
Changelog¶
See CHANGELOG.md for a changelog based on git commits.
For more detailed changelogs, please check the release changelogs.
Requirements¶
PHP¶
PHP greater than 7.4 is required.
Dependencies¶
No dependency is required.
Installation¶
The easiest way to install it is through Composer
composer require loophp/fpt
Usage¶
TODO
API¶
Static constructors¶
compose¶
Performs right-to-left function composition. The last argument may have any arity; the remaining arguments must be unary.
Warning
The result of compose is not automatically curried.
<?php
/**
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
// phpcs:disable Generic.Files.LineLength.TooLong
declare(strict_types=1);
namespace Example;
use loophp\fpt\FPT;
$closure = static fn (string $first, string $second): string => sprintf('My cats names are %s and %s.', $first, $second);
$composedClosure = FPT::compose()('strtoupper', $closure);
$composedClosure('Izumi', 'Nakano'); // "MY CATS NAMES ARE IZUMI AND NAKANO."
curryLeft¶
Returns a curried equivalent of the provided function.
<?php
/**
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Example;
use loophp\fpt\FPT;
$curryLeft = FPT::curryLeft()('explode');
[$firstName, $lastName] = $curryLeft(' ')('James Bond');
$curriedLeft = FPT::curryLeft()('explode', 3);
[$evil, $good] = $curriedLeft(',')('Jaws,James,Bond')(2);
curryRight¶
Returns a curried equivalent of the provided function.
<?php
/**
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Example;
use loophp\fpt\FPT;
$curryRight = FPT::curryRight()('explode');
[$firstName, $lastName] = $curryRight('James Bond')(' ');
$curryRight = FPT::curryRight()('explode', 3);
[$evil, $good] = $curryRight(2)('Jaws,James,Bond')(',');
filter¶
flip¶
Returns a new function much like the supplied one, except that the first two arguments’ order is reversed.
<?php
/**
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Example;
use loophp\fpt\FPT;
$f = static fn (string ...$x): string => implode('', $x);
$flip = FPT::flip()($f)('a', 'b', 'c'); // bac
fold¶
Returns a single item by iterating through the list, successively calling the reducer function and passing it an accumulator value and the current value from the array, and then passing the result to the next call.
The iterator function receives two values: $acc
and $value
.
<?php
/**
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Example;
include __DIR__ . '/../../../vendor/autoload.php';
use loophp\fpt\FPT;
$reducer = static fn (callable $a) => static fn (callable $b) => static fn (...$xs) => $a($b(...$xs));
$accumulator = static fn ($v): string => sprintf('[%s]', $v);
$callable = ['strtoupper', static fn ($s): string => sprintf('%s%s', $s, $s)];
FPT::fold()($reducer)($accumulator)(...$callable)('hello'); // [HELLOHELLO]
identity¶
A function that does nothing but return the parameter supplied to it.
<?php
/**
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Example;
use loophp\fpt\FPT;
FPT::identity()('foo'); // foo
map¶
Takes a function and an iterable, applies the function to each of the iterable values, and yield the result.
nary¶
Wraps a function of any arity in a function that accepts exactly n parameters. Any extraneous parameters will not be passed to the supplied function.
<?php
/**
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Example;
use loophp\fpt\FPT;
$closure = static fn (...$args): string => implode(';', $args);
$newClosure = FPT::nary()(1)($closure);
$newClosure('a', 'b'); // a
$newClosure = FPT::nary()(2)($closure);
$newClosure('a', 'b'); // a;b
not¶
Wraps a function in a function that returns the ! of the original function return value.
<?php
/**
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Example;
use loophp\fpt\FPT;
$closure = static fn (string $left, string $right): bool => $left === $right;
$closure('a', 'b'); // false
$closure('a', 'a'); // true
$notClosure = FPT::not()($closure);
$notClosure('a', 'b'); // true
$notClosure = FPT::not()($closure);
$notClosure('a', 'a'); // false
operator¶
Apply an operator on two arguments.
This method is curried and take first the operator, then its left and right members.
Available operators are constants in Operator class.
<?php
/**
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Example;
use loophp\fpt\FPT;
use loophp\fpt\Operator;
FPT::operator()(Operator::OP_PLUS)(40)(2); // 42
partialLeft¶
partialRight¶
reduce¶
thunk¶
uncurry¶
Tests, code quality and code style¶
Every time changes are introduced into the library, Github Actions run the tests.
Tests are written with PHPSpec and you can find the coverage percentage on a badge on the README file.
PHPInfection is also triggered used to ensure that your code is properly tested.
The code style is based on PSR-12 plus a set of custom rules. Find more about the code style in use in the package drupol/php-conventions.
A PHP quality tool, Grumphp, is used to orchestrate all these tasks at each commit on the local machine, but also on the continuous integration tools.
To run the whole tests tasks locally, do
composer grumphp
or
./vendor/bin/grumphp run
Here’s an example of output that shows all the tasks that are setup in Grumphp and that will check your code
$ ./vendor/bin/grumphp run
GrumPHP is sniffing your code!
Running task 1/14: SecurityChecker... ✔
Running task 2/14: Composer... ✔
Running task 3/14: ComposerNormalize... ✔
Running task 4/14: YamlLint... ✔
Running task 5/14: JsonLint... ✔
Running task 6/14: PhpLint... ✔
Running task 7/14: TwigCs... ✔
Running task 8/14: PhpCsAutoFixerV2... ✔
Running task 9/14: PhpCsFixerV2... ✔
Running task 10/14: Phpcs... ✔
Running task 11/14: Psalm... ✔
Running task 12/14: PhpStan... ✔
Running task 13/14: Phpspec... ✔
Running task 14/14: Infection... ✔
$
Contributing¶
See the file CONTRIBUTING.md but feel free to contribute to this library by sending Github pull requests.