linepogl / impartial-pipes
A PHP library with partial functions suitable for the pipe operator
Requires
- php: ^8.4
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.85
- linepogl/should: dev-main
- phpstan/phpstan: ^2.1
- phpunit/phpunit: ^12.3
README
A PHP library providing partial functions suitable for the pipe operator.
$users
|> p_filter(static fn (User $user) => $user->isAdmin)
|> p_order_by(static fn (User $x) => $user->age, descending: true)
|> p_then_by(static fn (User $x) => $user->name)
|> p_map(static fn (User $user) => $user->email)
|> p_unique()
|> p_skip(5)
|> p_take(10)
|> p_implode(';')
Features
- All the produced partial functions have exactly one argument, as the pipe operator expects.
- Type checking with phpstan and generics.
- Immutability and lazy evaluation.
- Iterating with rewindable generators without creating copies of the data. Fallback to array functions when no copies are involved.
- 100% test coverage.
Installation
composer require linepogl/impartial-pipes
Why use it?
The pipe operator of PHP is a great way to chain functions together. However, it is not perfect because it requires that the chained functions accept exactly one mandatory argument. When this is not the case, we have to resort to workarounds by wrapping the functions in a closure.
// PHP 8.5 $array |> static fn ($in) => array_map(static fn (int $x) => $x * $x), $in) |> static fn ($in) => array_filter($in, static fn (int $x) => $x % 2 === 1)
This might be solved in a future version of PHP, with a proposed syntax like this:
// PHP > 8.5, if accepted $array |> array_map(static fn (int $x) => $x * $x), ...) |> array_filter(..., static fn (int $x) => $x % 2 === 1)
This is a step in the right direction. However, it is still not perfect:
- We still have to remember the infamously inconsistent order of the arguments.
- It works only for arrays, but not for iterables, because the standard library offers too few functions for that.
With Impartial Pipes, we can write the same code as this:
// PHP 8.5, with Impartial Pipes $iterable |> p_map(static fn (int $x) => $x * $x) |> p_filter(static fn (int $x) => $x % 2 === 1)
Can I use it before PHP 8.5?
Yes, you can!
Even if the pipe operator is not available, partial functions can still be used. As a result, we get a syntax that is slightly more consistent than that of the standard library. On top of that, we can use these functions with both arrays and iterables.
// PHP 8.4, with Impartial Pipes p_filter(static fn (int $x) => $x % 2 === 1)( p_map(static fn (int $x) => $x * $x)( $iterable ) )
Alternatively, you can use the pipe
function as syntax sugar. In this case, the order of the operations is the same as in the pipe operator.
// PHP 8.4, with Impartial Pipes pipe($iterable) ->to(p_filter(static fn (int $x) => $x % 2 === 1)) ->to(p_map(static fn (int $x) => $x * $x))
Full Reference
Combining partial functions
Mapping partial functions
Filtering partial functions
- p_filter
- p_filter_out
- p_filter_out_nulls
- p_unique
- p_take
- p_while
- p_while_not_null
- p_skip
- p_skip_while
Reducing partial functions
- p_any
- p_all
- p_count
- p_first
- p_first_or
- p_first_key
- p_first_key_or
- p_last
- p_last_or
- p_last_key
- p_last_key_or
- p_sum
- p_implode
- p_to_array