New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce Expression Pipe Operator #6455

Closed
dlreeves opened this Issue Oct 30, 2015 · 16 comments

Comments

Projects
None yet
@dlreeves
Contributor

dlreeves commented Oct 30, 2015

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()
@steelbrain

This comment has been minimized.

Show comment
Hide comment
@steelbrain

steelbrain Oct 31, 2015

Contributor

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?
Contributor

steelbrain commented Oct 31, 2015

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

This comment has been minimized.

Show comment
Hide comment
@Orvid

Orvid Oct 31, 2015

Contributor

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.

Contributor

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.

@SiebelsTim

This comment has been minimized.

Show comment
Hide comment
@SiebelsTim

SiebelsTim Oct 31, 2015

Contributor

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.

Contributor

SiebelsTim commented Oct 31, 2015

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.

@dlreeves

This comment has been minimized.

Show comment
Hide comment
@dlreeves

dlreeves Oct 31, 2015

Contributor

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

Contributor

dlreeves commented Oct 31, 2015

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

@Daniel15

This comment has been minimized.

Show comment
Hide comment
@Daniel15

Daniel15 Nov 2, 2015

Member

Why not just use collections?

Member

Daniel15 commented Nov 2, 2015

Why not just use collections?

@simonwelsh

This comment has been minimized.

Show comment
Hide comment
@simonwelsh

simonwelsh Nov 2, 2015

Contributor

@Daniel15 that's covered in the linked blog post

Contributor

simonwelsh commented Nov 2, 2015

@Daniel15 that's covered in the linked blog post

@Daniel15

This comment has been minimized.

Show comment
Hide comment
@Daniel15

Daniel15 Nov 2, 2015

Member

Thanks, somehow I totally missed that link.

Member

Daniel15 commented Nov 2, 2015

Thanks, somehow I totally missed that link.

@mglinski

This comment has been minimized.

Show comment
Hide comment
@mglinski

mglinski 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.

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

@paulbiss

This comment has been minimized.

Show comment
Hide comment
@paulbiss

paulbiss Feb 17, 2016

Contributor

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

Contributor

paulbiss commented Feb 17, 2016

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

@dlreeves

This comment has been minimized.

Show comment
Hide comment
@dlreeves

dlreeves Feb 18, 2016

Contributor

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
})(1)
Contributor

dlreeves commented Feb 18, 2016

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
})(1)
@dlreeves

This comment has been minimized.

Show comment
Hide comment
@dlreeves

dlreeves Feb 29, 2016

Contributor

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

Contributor

dlreeves commented Feb 29, 2016

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 Mar 1, 2016

Merged

Pipe operator #285

@aorenste aorenste closed this Sep 27, 2016

@beerendlauwers

This comment has been minimized.

Show comment
Hide comment
@beerendlauwers

beerendlauwers Apr 21, 2017

@dlreeves Is there support for this in h2tp ?

@dlreeves Is there support for this in h2tp ?

@SiebelsTim

This comment has been minimized.

Show comment
Hide comment
@SiebelsTim

SiebelsTim Apr 21, 2017

Contributor

@beerendlauwers h2tp is not supported anymore. And development stopped some time ago. This feature is not supported.

Internal Error:
error unparsing /var/www/test/test.hh. unsupported construct in Unparser:  Dollardollar
Contributor

SiebelsTim commented Apr 21, 2017

@beerendlauwers h2tp is not supported anymore. And development stopped some time ago. This feature is not supported.

Internal Error:
error unparsing /var/www/test/test.hh. unsupported construct in Unparser:  Dollardollar
@beerendlauwers

This comment has been minimized.

Show comment
Hide comment
@beerendlauwers

beerendlauwers Apr 25, 2017

@SiebelsTim That's a shame. Any reason why h2tp is no longer being developed?

@SiebelsTim That's a shame. Any reason why h2tp is no longer being developed?

@SiebelsTim

This comment has been minimized.

Show comment
Hide comment
@SiebelsTim

SiebelsTim Apr 25, 2017

Contributor

@beerendlauwers See https://www.facebook.com/groups/hhvm.general/permalink/803847413101987/
The infrastructure around it was removed, however a replacement is on the way. A replacement for h2tp however is not yet confirmed.

Contributor

SiebelsTim commented Apr 25, 2017

@beerendlauwers See https://www.facebook.com/groups/hhvm.general/permalink/803847413101987/
The infrastructure around it was removed, however a replacement is on the way. A replacement for h2tp however is not yet confirmed.

@beerendlauwers

This comment has been minimized.

Show comment
Hide comment
@beerendlauwers

beerendlauwers Apr 25, 2017

@SiebelsTim Ok, thank you for the clarification. I have to work with Drupal codebases that cannot be hosted on HHVM-capable servers, so h2tp seemed the perfect solution to be able to use Hack. I will look into static analysis tools for PHP 5 / 7, and I'll probably write a simple preprocessor myself to get some nicer syntax.

@SiebelsTim Ok, thank you for the clarification. I have to work with Drupal codebases that cannot be hosted on HHVM-capable servers, so h2tp seemed the perfect solution to be able to use Hack. I will look into static analysis tools for PHP 5 / 7, and I'll probably write a simple preprocessor myself to get some nicer syntax.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment