-
Notifications
You must be signed in to change notification settings - Fork 3.1k
Description
This is a part of the Hack array proposal
To put simply, writing in a functional style with arrays can produce some ugly code. Consider this example
// arrays
count(
array_filter(
array_map(
$x ==> $x->getNumber(),
array(...),
),
$x ==> is_even($x),
)
)
Since Collections are objects, methods calls can be chained to produce a more fluid interface. Here is the same piece of code written in terms of collections.
// collections
(Map {...})
->map($x ==> $x->getNumber())
->filter($x ==> is_even($x))
->count()
This code is considerably easier to read because the operations are chained one after another. First you map, then you filter, then you count. Functions do not have this property, so any functional API involving arrays will lead to the deep nesting demonstrated earlier. This can be fixed by introducing some sugar syntax borrowed from functional languages such as F# and OCaml.
// map -> filter -> count
array(...)
|> array_map($x ==> $x->getNumber(), $$)
|> array_filter($$, $x ==> is_even($x))
|> count($$)
The |>
operator will be known as the expression pipe operator. It takes the value on the left of the operator, evaluates it and passes the result into the placeholder on the right of the operator. This is analogous to piping in a Linux command line. The $$
is the placeholder for where the result of the left expression will be substituted. The beauty of this solution is that it can be transformed trivially to the nested function style by the HHVM compiler, meaning this will have zero runtime impact.
This can also improve the utility of Collections or other APIs that rely heavily on method chaining. Now static utility methods can be added without breaking the style.
function compact<T>(Traversable<?T> $vector): Vector<T> {
...
}
// What is happening here?
compact($vec->map($x => $x?->foo()))->count();
// Maintain chaining style
$vec
->map($x ==> $x?->foo())
|>compact($$)
->count()