diff --git a/docs/functional-php.md b/docs/functional-php.md index cc595a6a..9e0b8973 100644 --- a/docs/functional-php.md +++ b/docs/functional-php.md @@ -89,7 +89,7 @@ preferred way starting with PHP 5.6. ```php use function Functional\map; -map(range(0, 100), function($v) {return $v + 1;}); +$emails = map($users, fn ($user) => $users->getEmail()); ``` # Function overview @@ -100,14 +100,9 @@ map(range(0, 100), function($v) {return $v + 1;}); ``Functional\every(array|Traversable $collection, callable $callback = null)`` ```php -isActive();})) { - invoke($users, 'setActive', [false]); -} +$allUsersAreActive = every($users, fn ($user, $key, $collection) => $user->isActive()); ``` If `$callback` is not provided then the `id()` function is used and `every` will return true if every value in the collection is truthy. @@ -117,12 +112,9 @@ If `$callback` is not provided then the `id()` function is used and `every` will ``bool Functional\some(array|Traversable $collection, callable $callback = null)`` ```php -isFriendOf($me);})) { - // One of those users is a friend of me -} +$iHaveAtLeastOneFriend = some($users, fn ($user, $key, $collection) => $user->isFriendOf($me)); ``` If `$callback` is not provided then the `id()` function is used and `some` will return true if at least one value in the collection is truthy. @@ -132,12 +124,9 @@ If `$callback` is not provided then the `id()` function is used and `some` will ``bool Functional\none(array|Traversable $collection, callable $callback)`` ```php -isActive();})) { - // Do something with a whole list of inactive users -} +$noUsersAreActive = none($users, fn ($user, $key, $collection) => $user->isActive()); ``` If `$callback` is not provided then the `id()` function is used and `none` will return true if every value in the collection is falsey. @@ -149,13 +138,10 @@ If `$callback` is not provided then the `id()` function is used and `none` will ``array Functional\reject(array|Traversable $collection, callable $callback = null)`` ```php -isActive(); -}; +$fn = fn ($user, $key, $collection) => $user->isActive(); $activeUsers = select($users, $fn); $inactiveUsers = reject($users, $fn); ``` @@ -176,14 +162,10 @@ Alias for `Functional\select()` is `Functional\filter()` ``array Functional\drop_last(array|Traversable $collection, callable $callback)`` ```php - $index < 3; // All users except the first three drop_first($users, $fn); // First three users @@ -198,7 +180,6 @@ Returns true or false if all elements in the collection are strictly true or fal ``bool Functional\false(array|Traversable $collection)`` ```php - strcmp($left, $right)); // Sorts a collection alphabetically, preserving keys -sort($collection, function($left, $right) { - return strcmp($left, $right); -}, true); +sort($collection, fn ($left, $right) => strcmp($left, $right), true); // Sorts a collection of users by age -sort($collection, function($user1, $user2) { +sort($collection, function ($user1, $user2) { if ($user1->getAge() == $user2->getAge()) { return 0; } @@ -319,22 +293,16 @@ Partial application is a concept where a higher-order function returns a new fun to the new function. Let’s have a look at the following simple function that takes two parameters and subtracts them: ```php -$subtractor = function ($a, $b) { - return $a - $b; -}; -$subtractor(10, 20); // -> -10 +$subtractor = fn ($a, $b) => $a - $b; +$subtractor(20, 10); // 10 ``` The same function can be reduced to two nested functions each with a single argument: ```php -$subtractor = function ($a) { - return function ($b) use ($a) { - return $a - $b; - }; -} -$partiallyAppliedSubtractor = $subtractor(10); -$partiallyAppliedSubtractor(20); // -> -10 +$subtractor = fn ($a) => fn ($b) => $a - $b; +$partiallyAppliedSubtractor = $subtractor(20); +$partiallyAppliedSubtractor(10); // 10 ``` @@ -346,8 +314,8 @@ our example again, this time using `partial_left`: ```php use function Functional\partial_left; -$partiallyAppliedSubtractor = partial_left($subtractor, 10); -$partiallyAppliedSubtractor(20); // -> -10 +$partiallyAppliedSubtractor = partial_left($subtractor, 20); +$partiallyAppliedSubtractor(10); // 10 ``` A slightly different example with `partial_right` where we do the calculation `20 - 10`: @@ -355,8 +323,8 @@ A slightly different example with `partial_right` where we do the calculation `2 ```php use function Functional\partial_right; -$partiallyAppliedSubtractor = partial_right($subtractor, 10); -$partiallyAppliedSubtractor(20); // -> 10 +$partiallyAppliedSubtractor = partial_right($subtractor, 20); +$partiallyAppliedSubtractor(10); // 10 ``` ## ary() @@ -371,12 +339,11 @@ For example: use function Functional\ary; use function Functional\map; -# This fails because map calls it's callable with the element, -# index and the whole collection -# map($array, 'ucfirst'); +// This fails because map calls its callable with the element, index and the whole collection +// map($array, 'ucfirst'); -# Using `ary` -map($array, ary('ucfirst', 1')); # Passes only the first argument to `ucfirst` +// Using `ary` +map($array, ary('ucfirst', 1)); // Passes only the first argument to `ucfirst` ``` ## partial_any() @@ -395,9 +362,8 @@ $elements = [ 'joanna', 'patrick', ]; -$selected = select($elements, function ($element) { - return substr_count($element, 'jo'); -}); + +$selected = select($elements, fn ($name) => substr_count($name, 'jo')); ``` Instead of writing that slightly obnoxious callback, let’s use a partially applied function: @@ -413,6 +379,7 @@ $elements = [ 'joanna', 'patrick', ]; + $selected = select($elements, partial_any('substr_count', …, 'jo')); ``` @@ -425,12 +392,8 @@ list of objects by a predicate that belongs to the object: ```php use function Functional\select; -use function Functional\partial_method; -$users = [new User(), new User()]; -$registeredUsers = select($users, function (User $user) { - return $user->isRegistered(); -}); +$registeredUsers = select($users, fn ($user) => $user->isRegistered()); ``` We can rewrite the above example like this: @@ -439,7 +402,6 @@ We can rewrite the above example like this: use function Functional\select; use function Functional\partial_method; -$users = [new User(), new User()]; $registeredUsers = select($users, partial_method('isRegistered')); ``` @@ -463,7 +425,7 @@ function div($dividend, $divisor) { } $average = converge('div', ['array_sum', 'count']); -$average([1, 2, 3, 4]); // -> 2.5 +$average([1, 2, 3, 4]); // 2.5 ``` The returned function, in the above example it is named `$average`, passes each of its arguments to each branching function. `$average` then takes the return values of all the branching functions and passes each one as an argument to the converging function. The return value of the converging function is the return value of `$average`. @@ -483,8 +445,8 @@ If we revisit the example used for partial application, the curried version woul use function Functional\curry; $curriedSubtractor = curry($subtractor); -$subtractFrom10 = $curriedSubtractor(10) -$subtractFrom10(20); // -> -10 +$subtractFrom10 = $curriedSubtractor(20); +$subtractFrom10(10); // 10 ``` The difference becomes more salient with functions taking more than two parameters : @@ -492,16 +454,12 @@ The difference becomes more salient with functions taking more than two paramete ```php use function Functional\curry; -function add($a, $b, $c, $d) { - return $a + $b + $c + $d; -} - -$curriedAdd = curry('add'); +$curriedAdd = curry(fn ($a, $b, $c, $d) => $a + $b + $c + $d); $add10 = $curriedAdd(10); $add15 = $add10(5); $add42 = $add15(27); -$add42(10); // -> 52 +$add42(10); // 52 ``` Since PHP allows for optional parameters, you can decide if you want to curry them or not. The default is to not curry them. @@ -509,15 +467,13 @@ Since PHP allows for optional parameters, you can decide if you want to curry th ```php use function Functional\curry; -function add($a, $b, $c = 10) { - return $a + $b + $c; -} +$add = fn ($a, $b, $c = 10) => $a + $b + $c; -// Curry only required parameters, the default, $c will always be 10 -$curriedAdd = curry('add', true); +// Curry only required parameters. The default $c will always be 10. +$curriedAdd = curry($add, true); // This time, 3 parameters will be curried. -$curriedAddWithOptional = curry('add', false); +$curriedAddWithOptional = curry($add, false); ``` Starting with PHP7 and the implementation of the ["Uniform variable syntax"](https://wiki.php.net/rfc/uniform_variable_syntax), you can greatly simplify the usage of curried functions. @@ -525,12 +481,8 @@ Starting with PHP7 and the implementation of the ["Uniform variable syntax"](htt ```php use function Functional\curry; -function add($a, $b, $c, $d) { - return $a + $b + $c + $d; -} - -$curriedAdd = curry('add'); -$curriedAdd(10)(5)(27)(10); // -> 52 +$curriedAdd = curry(fn ($a, $b, $c, $d) => $a + $b + $c + $d); +$curriedAdd(10)(5)(27)(10); // 52 ``` _Note, that you cannot use `curry` on a flipped function. `curry` uses reflection to get the number of function arguments, but this is not possible on the function returned from `flip`. Instead use `curry_n` on flipped functions._ @@ -542,18 +494,14 @@ _Note, that you cannot use `curry` on a flipped function. `curry` uses reflectio ```php use function Functional\curry_n; -function add($a, $b, $c, $d) { - return $a + $b + $c + $d; -} - -$curriedAdd = curry_n(2, 'add'); +$curriedAdd = curry_n(2, fn ($a, $b, $c, $d) => $a + $b + $c + $d); $add10 = $curriedAdd(10); $add15 = $add10(5); -$add15(27, 10); // -> 52 +$add15(27, 10); // 52 ``` -Note that if you given a parameter bigger than the real number of parameters of your function, all extraneous parameters will simply be passed but ignored by the original function. +Note that if you give a parameter bigger than the real number of parameters of your function, all extraneous parameters will simply be passed but ignored by the original function. # Access functions @@ -566,18 +514,16 @@ Invoke a callback on a value if the value is not null. ``Function\with(mixed $value, callable $callback, bool $invokeValue = true, mixed $default = null): mixed`` ```php -doSomethingWithValue($value); +$retval = with(create_user('John Doe'), function ($user) { + send_welcome_email($user); return 'my_result'; }); ``` -`with()` returns whatever the callback returns. In the above example -`$retval` would be `'my_result'`. +`with()` returns whatever the callback returns. In the above example `$retval` would be `'my_result'`. If the value of `$value` is `null`, `with()` will return `$default` which defaults to be `null`. @@ -586,11 +532,10 @@ If the value of `$value` is `null`, `with()` will return `$default` which defaul ``mixed Functional\invoke_if(mixed $object, string $methodName[, array $methodArguments, mixed $defaultValue])`` ```php -updateLocation('Munich', 'Germany') ``` @@ -658,7 +598,6 @@ Fetch a single property from a collection of objects or arrays. ``array Functional\pluck(array|Traversable $collection, string|integer|float|null $propertyName)`` ```php - 1, 'two' => 2, 'three' => 3]; -pick($array, 'one'); // -> 1 -pick($array, 'ten'); // -> null -pick($array, 'ten', 10); // -> 10 +pick($array, 'one'); // 1 +pick($array, 'ten'); // null +pick($array, 'ten', 10); // 10 ``` @@ -688,7 +626,6 @@ Returns the first index holding specified value in the collection. Returns false ``array Functional\first_index_of(array|Traversable $collection, mixed $value)`` ```php - 1, 'baz' => 3] @@ -741,8 +674,6 @@ $array = select_keys(['foo' => 1, 'bar' => 2, 'baz' => 3], ['foo', 'baz']); Returns an array containing only those entries in the array/Traversable whose key is not in the supplied keys. ```php - 2] @@ -756,7 +687,9 @@ Creates a slice of `$collection` with `$count` elements taken from the beginning ``array Functional\take_left(Traversable|array $collection, int $count)`` ```php -Functional\take_left([1, 2, 3], 2); // [1, 2] +use function Functional\take_left; + +take_left([1, 2, 3], 2); // [1, 2] ``` ## take_right() @@ -768,8 +701,10 @@ This function will reorder and reset the integer array indices by default. This ``array Functional\take_right(Traversable|array $collection, int $count, bool $preserveKeys = false)`` ```php -Functional\take_right([1, 2, 3], 2); // [2, 3] -Functional\take_right(['a', 'b', 'c'], 2, true); // [1 => 'b', 2 => 'c'] +use function Functional\take_right; + +take_right([1, 2, 3], 2); // [2, 3] +take_right(['a', 'b', 'c'], 2, true); // [1 => 'b', 2 => 'c'] ``` # Function functions @@ -784,11 +719,11 @@ Retry a callback until the number of retries are reached or the callback does no use function Functional\retry; use function Functional\sequence_exponential; -assert_options(ASSERT_CALLBACK, function () {throw new Exception('Assertion failed');}); +assert_options(ASSERT_CALLBACK, fn () => throw new Exception('Assertion failed')); // Assert that a file exists 10 times with an exponential back-off retry( - function() {assert(file_exists('/tmp/lockfile'));}, + fn () => assert(file_exists('/tmp/lockfile')), 10, sequence_exponential(1, 100) ); @@ -804,9 +739,7 @@ use function Functional\sequence_linear; // Poll if a file exists for 10,000 microseconds with a linearly growing back-off starting at 100 milliseconds poll( - function() { - return file_exists('/tmp/lockfile'); - }, + fn () => file_exists('/tmp/lockfile'), 10000, sequence_linear(100, 1) ); @@ -820,15 +753,8 @@ Return a new function that captures the return value of $callback in $result and ```php use function Functional\capture; -$fn = capture( - function() { - return 'Hello world'; - }, - $result -); - +$fn = capture(fn () => 'Hello world', $result); $fn(); - var_dump($result); // 'Hello world' ``` @@ -839,14 +765,11 @@ Return a new function that composes multiple functions into a single callable ```php use function Functional\compose; -$plus2 = function ($x) { return $x + 2; }; -$times4 = function ($x) { return $x * 4; }; +$plus2 = fn ($x) => $x + 2; +$times4 = fn ($x) => $x * 4; $composed = compose($plus2, $times4); - -$result = array_map($composed, array(1, 2, 5, 8)); - -var_dump($result); // array(12, 16, 28, 40) +array_map($composed, [1, 2, 5, 8]); // [12, 16, 28, 40] ``` @@ -856,19 +779,17 @@ Return an new function that decorates given function with tail recursion optimiz ```php - $to) { return $acc; } - return $sum_of_range($from + 1, $to, $acc + $from); + + return $sumOfRange($from + 1, $to, $acc + $from); }); -var_dump($sum_of_range(1, 10000)); // 50005000; - +$sumOfRange(1, 10000); // 50005000; ``` ## flip() @@ -879,30 +800,22 @@ use function Functional\flip; use function Functional\curry; $filter = curry(flip('Functional\filter')); -$get_even = $filter(function($number) { - return $number % 2 == 0; -}); - -var_dump($get_even([1, 2, 3, 4])); // [2, 4] - +$getEven = $filter(fn ($number) => $number % 2 === 0); +$getEven([1, 2, 3, 4]); // [2, 4] ``` _Note, that you cannot use `curry` on a flipped function. `curry` uses reflection to get the number of function arguments, but this is not possible on the function returned from `flip`. Instead use `curry_n` on flipped functions._ ## not -Return a new function which takes the same arguments as the original function, but returns the logical negation of it's result. +Return a new function which takes the same arguments as the original function, but returns the logical negation of its result. ```php use function Functional\not; -$is_even = function ($number) { - return $number % 2 == 0; -}; -$is_odd = not($is_even); - -var_dump($is_odd(1)); // true -var_dump($is_odd(2)); // false - +$isEven = fn ($number) => $number % 2 === 0; +$isOdd = not($isEven); +$isOdd(1); // true +$isOdd(2); // false ``` ## Other @@ -955,17 +868,13 @@ final partition. ``array Functional\partition(array|Traversable $collection, callable $callback ...)`` ```php -isAdmin(); - }, - function($user) { - return $user->isGuest(); - }); + fn ($user) => $user->isAdmin(), + fn ($user) => $user->isGuest() +); ``` @@ -975,12 +884,9 @@ Splits a collection into groups by the index returned by the callback ``array Functional\group(array|Traversable $collection, callable $callback)`` ```php -getGroup()->getName(); -}); +$groupedUser = group($users, fn ($user) => $user->getGroup()->getName()); ``` @@ -992,20 +898,15 @@ Recombines arrays by index and applies a callback optionally ``array Functional\zip_all(array|Traversable $collection1[, array|Traversable ...[, callable $callback]])`` ```php - $one . '|' . $two +); // ['one|1', 'two|2', 'three|3'] ``` ``zip()`` uses the keys of the first input array. ``zip_all()`` uses all the keys present in the input arrays. @@ -1016,11 +917,9 @@ Takes a nested combination of collections and returns their contents as a single ``array Functional\flatten(array|Traversable $collection)`` ```php - $reduction . $value, 2); // '223' +$str = reduce_right([2, 3], fn ($value, $index, $collection, $reduction) => $reduction . $value, 2); // '232' ``` @@ -1055,7 +946,9 @@ $str = reduce_right([2, 3], function($value, $index, $collection, $reduction) { Insert a given value between each element of a collection. ```php -intersperse(['a', 'b', 'c'], '-') === ['a', '-', 'b', '-', 'c'] +use Functional\intersperse; + +intersperse(['a', 'b', 'c'], '-'); // ['a', '-', 'b', '-', 'c']; ``` ## Other @@ -1076,18 +969,11 @@ Returns a new function that will call `$then` if the return of `$if` is truthy, All three functions will be called with the given argument. ```php - "Yes, {$value} is greater than 1"; +$ifItIsNot = fn ($value) => "Nop, {$value} isn't greater than 1"; $message = if_else(greater_than(1), $ifItIs, $ifItIsNot); @@ -1103,22 +989,12 @@ Returns a new function that behaves like a match operator. It stops on the first match and if none of the conditions matches, `null` is returned. ```php - "At {$age} you go to preschool"; +$primary = fn ($age) => "At {$age} you go to primary school"; +$secondary = fn ($age) => "At {$age} you go to secondary school"; $stage = match([ [greater_than_or_equal(12), $secondary], @@ -1126,9 +1002,9 @@ $stage = match([ [greater_than_or_equal(4), $preschool], ]); -echo $stage(4); // With 4 you go to preschool -echo $stage(5); // With 5 you go to primary school -echo $stage(13); // With 13 you go to secondary school +echo $stage(4); // At 4 you go to preschool +echo $stage(5); // At 5 you go to primary school +echo $stage(13); // At 13 you go to secondary school ``` # Higher order comparison functions @@ -1152,31 +1028,28 @@ just a shortcut to `compare_on` as it composes the given key function with `spl_ Concatenates zero or more strings. ```php - 1 +$one(); // 1 ``` ## id() Proxy function that does nothing except returning its first argument. ```php -login(); + +tap(create_user('John Doe'), fn ($user) => send_welcome_email($user))->login(); ``` ## repeat() @@ -1197,11 +1068,11 @@ tap(User::create('John Doe'), function (User $user) { Creates and returns a function that can be used to execute the given closure multiple times. ```php -