The "pipe operator" |>
allows you to chain multiple function calls in a more convenient way.
$result = "Hello World" |> 'htmlentities' |> 'str_split' |> fn($x) => array_map('strtoupper', $x) |> fn($x) => array_filter($x, fn($v) => $v != 'O');
This RFC was already declined, but we're sharing it here as another test RFC, and because it'd be interesting to learn people's opinion about it.
With First-class callable syntax available since 8.1, it would now be possible to write it as below, which is much better then string names of functions:
$result = "Hello World" |> htmlentities(...) |> str_split(...) |> fn($x) => array_map(strtoupper(...), $x) |> fn($x) => array_filter($x, fn($v) => $v != 'O');
For me, the most important argument is that the pipeline pattern is a tried and tested pattern, that this RFC builds upon. A couple of examples:
This RFC adds syntax to make using these kinds of pattern much more convenient.
On top of that, there's the argument that multiple modern languages support a pipe operator:
Finally, I've had numerous occasions where a pipe operator would simplify my own code — I have more than a handful real life cases where this would be useful.
I see no immediate benefit of the proposed solution over the userland implementations. The RFC mentions a shopping cart example, but I don't think that's cleaner than using league/pipeline or Laravel's pipeline.
It's a bit messy for the simpler examples as well.
For example:
$result = "Hello World"
|> 'htmlentities'
|> 'str_split'
|> fn($x) => array_map('strtoupper', $x)
|> fn($x) => array_filter($x, fn($v) => $v != 'O');
Is 'nicer' since it has better readability and clearer syntax than:
$result = array_filter(
str_split(
strtoupper(
htmlentities("Hello World")
)
),
fn($v) => $v != '0'
);
I could have used it multiple times for array transformations. But the RFC still built on really ancient PHP behaviour (mapping functions as strings) and should be redone by fosucing only on modern syntax:
|> fn($x) => array_filter($x, fn($v) => $v != 'O')
|> str_split(...)
Still hard to read. No extra benefits.
The only clean solution is to use scalar types (string, int, float, boolean) and arrays like objects:
$result = "Hello World"->htmlentities()->split()->map(strtoupper(...))->filter(fn($v) => $v != 'O');
Chain, clean oop, readable, IDE hint, no value parameter, no prefixes and an opportunity to correct the functions inconsistency. It could works beside functions: strtoupper($name)
and $name->toUpper()
.
It's almost as messy as putting all the functions into each other.
The idea is a nice one, and one that I would welcome, but this proposal puts forward messy syntax that isn't clear!
I think complaints about the syntax being messy are really about the array manipulation functions being inherently hard to format nicely.
To be clear I agree that the proposed example isn't great, nested closures are never going to win any readabillity prizes. If however we look at any non-array based manipulation I think the readabillity is objectively better:
$name = 'my_user_name' |> fn (string $string): string => str_replace('_', ' ', $string) |> strtolower(...) |> ucwords(...) |> trim(...);
Or without first class callables:
$name = 'my_user_name' |> fn (string $string): string => str_replace('_', ' ', $string) |> fn (string $string): string => strtolower($string) |> fn (string $string): string => ucwords($string) |> fn (string $string): string => trim($string);
Bonus: this also adds runtime type checks to each step. Eg strreplace
returns string|array
I can't imagine anyone would think this is better:
$name = trim( ucwords( strtolower( str_replace('_', ' ', 'my_user_name') ) ) );
I think this would be a great improvement.
I prefer this:
$result = "Hello World" |> htmlentities(...) |> str_split(...) |> fn($x) => array_map(strtoupper(...), $x) |> fn($x) => array_filter($x, fn($v) => $v != 'O');
instead of this:
$result = "Hello World" |> 'htmlentities' |> 'str_split' |> fn($x) => array_map('strtoupper', $x) |> fn($x) => array_filter($x, fn($v) => $v != 'O');
this syntax is unclear, and functions who gets multiple arguments will not be properly supported.
No clear benefit and the syntax is simply just too messy.
need it
This will help make code more readable in situations when we have to chain many built-in functions and is a good entrypoint for making pure minimalistic functions a first class citizen in PHP.
Maybe if the function names didn't need to be in quotes... As it is certainly not.
A new syntax for declaring the “set” operation visibility of an object property
Interface Default Methods improves backwards compatibility when changing interfaces, but also add a way to have multi-inheritance in PHP.