10

GitHub - whsv26/functional: PHP Functional Programming library. Monads, common u...

 3 years ago
source link: https://github.com/whsv26/functional
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Functional PHP

PHP Functional Programming library. Monads and common use functions.

Documentation

Installation

Composer

$ composer require whsv26/functional

Enable psalm plugin (optional)

To improve type inference for particular functions

$ vendor/bin/psalm-plugin enable Fp\\Psalm\\FunctionalPlugin

Examples

  • Type assertions with Option monad
/**
 * Inferred type is Option<Foo> 
 */ 
$maybeFooMaybeNot = Option::do(function() use ($untrusted) {
    $notNull = yield Option::fromNullable($untrusted);
    yield proveTrue(is_array($notNull)); // Inferred type is array<array-key, mixed> 
    $list = yield proveList($notNull); // Inferred type is list<mixed>
    $nonEmptyList = yield proveNonEmptyList($list); // Inferred type is non-empty-list<mixed>
    $nonEmptyListOfFoo = yield proveNonEmptyListOf($nonEmptyList, Foo::class); // Inferred type is non-empty-list<Foo>
    $firstFoo = $nonEmptyListOfFoo[0]; // Inferred type is Foo

    return $firstFoo; // I'm sure it's Foo object
});

/**
 * Inferred type is Foo
 */
$foo = $maybeFooMaybeNot->getOrCall(fn() => new Foo(0))
  • Type safety
/**
 * 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);
$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)->filterNotNull();

/**
 * Inferred type is ArrayList<Foo>
 * Bar type was removed
 */
$onlyFoos = $withoutNulls->filter(fn($elem) => $elem instanceof Foo);
  • Covariance
class User {}
class Admin extends User {}

/**
* @param NonEmptyCollection<User> $collection
*/
function acceptUsers(NonEmptyCollection $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
$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
/**
 * @var Collection<int> $emptyCollection 
 */
$emptyCollection = getEmptyCollection();

$resultWithDefaultValue = $emptyCollection
    ->reduce(fn(int $accumulator, int $element) => $accumulator + $element)
    ->getOrElse(0);

/**
 * @return Option<float>
 */
function div(int $a, int $b): Option {
    return 0 === $b 
        ? Option::none()
        : Option::some($a / $b)
}

/**
 * It's possible there is no first collection element above zero
 * In this case the execution will short circuit (stop) 
 * And no Null Pointer Exception will be thrown
 */
$emptyCollection
    ->first(fn(int $elem) => $elem > 0)
    ->map(fn(int $elem) => $elem + 1)
    ->flatMap(fn(int $elem) => div($elem, $elem - 1))
    ->getOrElse(0)

Contribution

Build documentation

  1. Install dependencies
$ sudo apt install pandoc
  1. Generate doc from src
$ make

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK