fp4php / functional
PHP Functional Programming library
Installs: 43 464
Dependents: 12
Suggesters: 0
Security: 0
Stars: 191
Watchers: 10
Forks: 6
Open Issues: 0
Requires
- php: ^8.1
- ext-simplexml: *
Requires (Dev)
- fp4php/functional-psalm-plugin: ^1.0
- php-coveralls/php-coveralls: ~2.4.0
- phpunit/phpunit: ^9
- rregeer/phpunit-coverage-check: ^0.3.1
- symfony/process: ^5.2
- vimeo/psalm: ^5.7
- dev-master
- v6.0.0
- v5.x-dev
- v5.2.0
- v5.1.0
- v5.0.0
- v4.20.0
- v4.19.0
- v4.18.0
- v4.17.0
- v4.16.1
- v4.16.0
- v4.15.0
- v4.14.0
- v4.13.0
- v4.12.0
- v4.11.0
- v4.10.0
- v4.9.0
- v4.8.1
- v4.8.0
- v4.7.0
- v4.6.0
- v4.5.0
- v4.4.0
- v4.3.0
- v4.2.0
- v4.1.1
- v4.1.0
- v4.0.1
- v4.0.0
- v3.0.1
- v3.0.0
- v2.4.2
- v2.4.1
- v2.4.0
- v2.3.0
- v2.2.4
- v2.2.3
- v2.2.2
- v2.2.1
- v2.2.0
- v2.1.0
- v2.0.6
- v2.0.5
- v2.0.4
- v2.0.3
- v2.0.2
- v2.0.1
- v2.0.0
- v1.0.2
- v1.0.1
- v1.0
- dev-psalm-v5
- dev-generic-inference-experiment
- dev-feature/stack-overflow-related
This package is auto-updated.
Last update: 2024-11-22 17:55:44 UTC
README
PHP Functional Programming library. Monads and common use functions.
Documentation
Installation
Composer
Supported installation method is via composer:
$ composer require fp4php/functional
Psalm integration
Please refer to the fp4php/functional-psalm-plugin repository.
Overview
Typesafe and concise.
Powerful combination: Collections + Option monad.
<?php use Fp\Collections\ArrayList; use Fp\Functional\Option\Option; use function Fp\Evidence\of; use function Fp\Evidence\proveString; class PgSqlCurrencyArrayType extends Type { public function convertToDatabaseValue($value, AbstractPlatform $platform): string { $currencies = Option::fromNullable($value) ->filter(is_iterable(...)) ->getOrElse([]); return ArrayList::collect($currencies) ->flatMap(of(Currency::class)) ->map(fn(Currency $currency) => $currency->getCurrencyCode()) ->mkString('{', ',', '}'); } /** * @return ArrayList<Currency> */ public function convertToPHPValue($value, AbstractPlatform $platform): ArrayList { $csv = Option::fromNullable($value) ->flatMap(proveString(...)) ->map(fn(string $pgSqlArray) => trim($pgSqlArray, '{}')) ->getOrElse(''); return ArrayList::collect(explode(',', $csv)) ->filterMap($this->parseCurrency(...)); } /** * @return Option<Currency> */ public function parseCurrency(string $currencyCode): Option { return Option::try(fn() => Currency::of($currencyCode)); } }
Examples
- Type safety
<?php use Fp\Collections\NonEmptyLinkedList; /** * Inferred type is NonEmptyLinkedList<1|2|3> */ $collection = NonEmptyLinkedList::collectNonEmpty([1, 2, 3]); /** * Inferred type is NonEmptyLinkedList<int> * * Literal types are dropped after map transformation, * but NonEmpty collection prefix has been kept */ $mappedCollection = $collection->map(fn($elem) => $elem - 1); /** * Inferred type is LinkedList<positive-int> * NonEmpty prefix has been dropped */ $filteredCollection = $mappedCollection->filter(fn(int $elem) => $elem > 0);
<?php use Tests\Mock\Foo; use Tests\Mock\Bar; use Fp\Collections\NonEmptyArrayList; $source = [new Foo(1), null, new Bar(2)]; /** * Inferred type is ArrayList<Foo|Bar> * Null type was removed * NonEmpty prefix was removed */ $withoutNulls = NonEmptyArrayList::collectNonEmpty($source) ->filter(fn(Foo|Bar|null $elem) => null !== $elem); /** * Inferred type is ArrayList<Foo> * Bar type was removed */ $onlyFoos = $withoutNulls->filter(fn($elem) => $elem instanceof Foo);
- Covariance
<?php use Fp\Collections\NonEmptyLinkedList; class User {} class Admin extends User {} /** * @param NonEmptyLinkedList<User> $collection */ function acceptUsers(NonEmptyLinkedList $collection): void {} /** * @var NonEmptyLinkedList<Admin> $collection */ $collection = NonEmptyLinkedList::collectNonEmpty([new Admin()]); /** * You can pass collection of admins instead of users * Because of covariant template parameter */ acceptUsers($collection);
- Immutability
<?php use Fp\Collections\LinkedList; $originalCollection = LinkedList::collect([1, 2, 3]); /** * $originalCollection won't be changed */ $prependedCollection = $originalCollection->prepended(0); /** * $prependedCollection won't be changed */ $mappedCollection = $prependedCollection->map(fn(int $elem) => $elem + 1);
- Null safety
<?php use Fp\Functional\Option\Option; use Fp\Collections\ArrayList; /** * @var ArrayList<int> $collection */ $collection = getCollection(); /** * @return Option<float> */ function div(int $a, int $b): Option { return Option::when(0 !== $b, fn() => $a / $b); } /** * It's possible there is no first collection element above zero * or divisor is zero. * * In this case the execution will short circuit (stop) * and no Null Pointer Exception will be thrown. */ $collection ->first(fn(int $elem) => $elem > 0) ->map(fn(int $elem) => $elem + 1) ->flatMap(fn(int $elem) => div($elem, $elem - 1)) ->getOrElse(0)
<?php use Tests\Mock\Foo; use Fp\Functional\Option\Option; use function Fp\Evidence\proveTrue; use function Fp\Evidence\proveNonEmptyList; /** * Inferred type is Option<Foo> */ $maybeFooMaybeNot = Option::do(function() use ($untrusted) { // If $untrusted is not null then bind this value to $notNull $notNull = yield Option::fromNullable($untrusted); // If $notNull is non-empty-list<Tests\Mock\Foo> then bind this value to $nonEmptyListOfFoo $nonEmptyList = yield proveNonEmptyList($notNull, of(Foo::class)); // Continue computation if $nonEmptyList contains only one element yield proveTrue(1 === count($nonEmptyList)); // I'm sure it's Foo object return $nonEmptyList[0]; }); // Inferred type is Tests\Mock\Foo $foo = $maybeFooMaybeNot->getOrCall(fn() => new Foo(0));
Contribution
Build documentation
- Install dependencies
$ sudo apt install pandoc
- Generate doc from src
$ make