Introduce Expression Pipe Operator #6455

dlreeves opened this Issue Oct 30, 2015 · 11 comments


None yet

10 participants


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
      $x ==> $x->getNumber(),
    $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))

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_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
  ->map($x ==> $x?->foo())

IMO the syntax of this is too confusing. It would be nice if you would provide us with an example about how this code is going to be transpiled like

// Source:
$arr = get_some_array()
  |> do_something($$)
// Output
$arr = do_something(get_some_array());

Other questions I have in mind are

  • Does this operator work on the same line? or is a new line a requirement?
  • Can we use this for non-array things?
Orvid commented Oct 31, 2015

The syntax of this is, while not quite as clean as D's Uniform Function Call Syntax (UFCS), is far cleaner than the alternative of not having anything. Due to some limitations of UFCS, in particular that the argument that's being chained on ($$ in this proposal) must be the first parameter, I think this is a better way of doing it.


Either I misunderstood something, or you have a bug in your last example. I assume you didn't want to pass an argument to the count method.


@SiebelsTim - Thanks. Had it confused with the count function

@StefanKarpinski StefanKarpinski referenced this issue in JuliaLang/julia Oct 31, 2015

Function chaining #5571

Daniel15 commented Nov 2, 2015

Why not just use collections?


@Daniel15 that's covered in the linked blog post

Daniel15 commented Nov 2, 2015

Thanks, somehow I totally missed that link.

mglinski commented Nov 3, 2015

Damn, having a expression-pipe operator would be soooooo nice in vanilla php. Once you get this implemented, you should put in a RFC to add it for 7.x.

@jwatzman jwatzman added the hack label Nov 8, 2015

This is done on the hhvm side (992c816), I think we're still missing hack support. cc @dlreeves


Yep we are currently working on adding the type checker support. Initially we won't have support for this in h2tp (the dehackificator) because the transformation we do in the runtime is a bit more complicated than I presented in the original issue. Namely we cannot simply de-nest the operator into nested function calls because of potential side effects. Instead we create temporary variables to store the result of the left operand.

If there is demand for support of the future for h2tp I think the simplest way to do the transpile is nested closures, i.e.

1 |> $$ + $$

Could be translated to

(function($x) {
  return $x + $x

The type checker support for this feature has now landed. Please play around with it and report any issues you run into

@SiebelsTim SiebelsTim referenced this issue in hhvm/user-documentation Mar 1, 2016

Pipe operator #285

@aorenste aorenste closed this Sep 27, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment