DEV Community

david duymelinck
david duymelinck

Posted on

My thoughts on Spatie/Piper

When I saw a library for the pipe operator I needed to check it out.

People that read my posts before know I'm a big fan of the pipe operator feature. The main benefit is that it is less likely that the functions have side effects, because they don't depend on a master object that comes with a builder pattern.

The good

I like that they moved the collecting function inside the functions.

function map(callable $callback): Closure
{
    return function (array $items) use ($callback): array {
        $result = [];

        foreach ($items as $key => $value) {
            $result[$key] = $callback($value, $key);
        }

        return $result;
    };
}
Enter fullscreen mode Exit fullscreen mode

For people that are less aware how the pipe operator works in PHP 8.5 a short introduction.
The pipe operator passes a single variable on to whatever function is called next in the chain, [1, 2] |> array_sum(...). From the moment you want to use predefined arguments for a function, you need to wrap it in a function that collects that single variable, [1, 2] |> (fn($arr) => array_map(fn (int $i) => pow($i, 2), $arr)).
By moving the collecting function into the actual function they reduced the amount of code you need to write to [1, 2] |> map(fn (int $i) => pow($i, 2)).
In PHP 8.6 this verbosity problem will be solved, and then you can write [1, 2] |> array_map(fn (int $i) => pow($i, 2), ?).
But because the goal of the library is to mimic the Laravel builder pattern methods, I think it is a great choice.

I like that all the functions are in separate files. The functions.php file is there just to collect them all.

The misses

It is not to hard to see where the name comes from. The problem I have with it is that is too generic. Is it possible they are going to add other pipe operator categories?

That brings me to the following missed opportunity, why not split the library. There could be a collection-piper and string-piper library. This way the support functions and exceptions context will be clearer.
And you can choose which Laravel builder pattern you want replace, instead of adding functions you are never going to use.

Because functions are loaded upfront it might be a good idea to have a way to choose the ones the project is really going to use.
It is a problem that has not been addressed by PHP libraries and frameworks, including my own.
By having the functions in separate files this could be more easily achieved than having all the functions in a single file.

The negative consequence of having the collecting function in the functions, they can't be used for non pipe operator situations.

// plain use, pipe operator php 8.6 use
function map(array $items, callable $callback): array
{
    $result = [];

    foreach ($items as $key => $value) {
       $result[$key] = $callback($value, $key);
    }

    return $result;
}
// pipe operator PHP 8.5/8.6 use
function pMap(callable $callback): Closure
{
   return function(array $items) use ($callback) {
       return map($items, $callback);
   }
}
Enter fullscreen mode Exit fullscreen mode

Conclusion

I am a bit of an anti-abstractionist. If you can achieve something with no abstraction, array_map(fn (int $i) => pow($i, 2), [1, 2]), do that. As you can see from the example even the pipe operator can be an abstraction.
And that is why Laravel collections and string builders were never my favourite additions to the framework. Even while they have functions that are genuinely helpful. I used map in the examples, but for me that is not one of the helpful functions.

By having more pipe operator libraries the use of the functions will be more versatile than with builder pattern methods. But we need to be aware of the limitations if they only target the pipe operator.

This is quite a short post but I had to get those thoughts out of my head.

Top comments (0)