# [RFC] Pipe Operator

- Thread: https://externals.io/message/92960
- Original archive: https://news-web.php.net/php.internals/92960
- Started by Sara Golemon on 2016-04-29T19:58:57+00:00
- Messages: 102
- RFC: Pipe Operator (Inactive) — https://externals.io/rfc/pipe-operator.md
- Page 1 of 5: messages 1 to 25 (25 per page, add `?page=N` to the URL)
- Next page: https://externals.io/message/92960.md?page=2
- Reply by email to internals@lists.php.net
- More for agents: search with https://externals.io/search.md?q={query} · RFCs at https://externals.io/rfc.md · JSON API at https://externals.io/api · documentation at https://externals.io/llms.txt

---

## #92960 — Sara Golemon — 2016-04-29T19:58:57+00:00

URL: https://externals.io/message/92960#92960

This is one of my favorites out of HackLang.  It's pure syntactic
sugar, but it goes a long way towards improving readability.
https://wiki.php.net/rfc/pipe-operator

---

## #92963 — Bob Weinand — 2016-04-29T23:37:17+00:00

URL: https://externals.io/message/92960#92963 · In reply to: #92960

Hey,

> Am 29.04.2016 um 21:58 schrieb Sara Golemon <pollita@php.net>:
> 
> This is one of my favorites out of HackLang.  It's pure syntactic
> sugar, but it goes a long way towards improving readability.
> https://wiki.php.net/rfc/pipe-operator

Thanks for proposing, so that I am able to show what I don't like about the |> pipe operator:

In general, the pipe operator is hiding anti-patterns. It makes anti-patterns look nicer.

Take the first example:

$ret = 
  array_merge(
    $ret,
    getFileArg(
      array_map(
        function ($x) use ($arg) { return $arg . '/' . $x; },
        array_filter(
          scandir($arg),
          function ($x) { return $x !== '.' && $x !== '..'); }
        )
      )
    )
  );

Sure, it *looks* nicer with the pipe operator, if you really want to nest it. But that nesting is horrible in itself. It should be written as:

$files = array_filter(scandir($arg), function ($x) { $x != '.' && $x != '..'; });
$fullPath = array_map(function($x) use ($arg) { return "$arg/$x"; }, $files);
$allFiles = array_merge($allFiles, getFileArg($fullPath));

By limiting the nesting level to 2, the code gets itself much more readable and in addition, the variable names are self-explaining, so that you can jump right in.
As already mentioned, the |> variant looks nicer than the above, but it still lacks the most important part; documentation in form of obvious variable names after each small step.

It's always nice when code can look nicely, but this doesn't encourage writing understandable code. This RFC is just providing another way to shoot any future readers into the feet.
Readers want to quickly grasp what is going on. Having to follow a whole list of operations whose result is written to a variable $ret is telling them absolutely nothing.

TL;DR:
-1: The |> pipe operator encourages a write-only style.

Bob

---

## #92967 — Thomas Bley — 2016-04-29T23:54:40+00:00

URL: https://externals.io/message/92960#92967 · In reply to: #92963

Hey,

to me recursion and pipe operator looks really hard to read, I guess this is faster and better readable:

foreach (scandir($arg) as $x) {
  if ($x == '.' || $x == '..') {
    continue;
  }
  $x = $arg . '/' . $x;
  // inline getFileArg ...
  $allFiles[] = $x;
}

Regards
Thomas


Bob Weinand wrote on 30.04.2016 01:37:

> Hey,
> 
> > […]
> 
> Thanks for proposing, so that I am able to show what I don't like about the |>
> pipe operator:
> 
> In general, the pipe operator is hiding anti-patterns. It makes anti-patterns
> look nicer.
> 
> Take the first example:
> 
> $ret = 
>  array_merge(
>    $ret,
>    getFileArg(
>      array_map(
>        function ($x) use ($arg) { return $arg . '/' . $x; },
>        array_filter(
>          scandir($arg),
>          function ($x) { return $x !== '.' && $x !== '..'); }
>        )
>      )
>    )
>  );
> 
> Sure, it *looks* nicer with the pipe operator, if you really want to nest it.
> But that nesting is horrible in itself. It should be written as:
> 
> $files = array_filter(scandir($arg), function ($x) { $x != '.' && $x != '..';
> });
> $fullPath = array_map(function($x) use ($arg) { return "$arg/$x"; }, $files);
> $allFiles = array_merge($allFiles, getFileArg($fullPath));
> 
> By limiting the nesting level to 2, the code gets itself much more readable and
> in addition, the variable names are self-explaining, so that you can jump right
> in.
> As already mentioned, the |> variant looks nicer than the above, but it still
> lacks the most important part; documentation in form of obvious variable names
> after each small step.
> 
> It's always nice when code can look nicely, but this doesn't encourage writing
> understandable code. This RFC is just providing another way to shoot any future
> readers into the feet.
> Readers want to quickly grasp what is going on. Having to follow a whole list
> of operations whose result is written to a variable $ret is telling them
> absolutely nothing.
> 
> TL;DR:
> -1: The |> pipe operator encourages a write-only style.
> 
> Bob

---

## #92966 — Larry Garfield — 2016-04-29T23:56:52+00:00

URL: https://externals.io/message/92960#92966 · In reply to: #92963

> Hey,
>
> > […]
> Thanks for proposing, so that I am able to show what I don't like about the |> pipe operator:
>
> In general, the pipe operator is hiding anti-patterns. It makes anti-patterns look nicer.
>
> Take the first example:
>
> $ret =
>    array_merge(
>      $ret,
>      getFileArg(
>        array_map(
>          function ($x) use ($arg) { return $arg . '/' . $x; },
>          array_filter(
>            scandir($arg),
>            function ($x) { return $x !== '.' && $x !== '..'); }
>          )
>        )
>      )
>    );
>
> Sure, it *looks* nicer with the pipe operator, if you really want to nest it. But that nesting is horrible in itself. It should be written as:
>
> $files = array_filter(scandir($arg), function ($x) { $x != '.' && $x != '..'; });
> $fullPath = array_map(function($x) use ($arg) { return "$arg/$x"; }, $files);
> $allFiles = array_merge($allFiles, getFileArg($fullPath));
>
> By limiting the nesting level to 2, the code gets itself much more readable and in addition, the variable names are self-explaining, so that you can jump right in.
> As already mentioned, the |> variant looks nicer than the above, but it still lacks the most important part; documentation in form of obvious variable names after each small step.
>
> It's always nice when code can look nicely, but this doesn't encourage writing understandable code. This RFC is just providing another way to shoot any future readers into the feet.
> Readers want to quickly grasp what is going on. Having to follow a whole list of operations whose result is written to a variable $ret is telling them absolutely nothing.
>
> TL;DR:
> -1: The |> pipe operator encourages a write-only style.
>
> Bob

I disagree.  The absence of an intermediate variable name does not 
inherently hinder readability/debuggability (nor does its presence 
inherently improve it).  It just makes more work for the code author to 
come up with potentially pointless variable names.

What I see in |> is an end-run around the "can't call methods on arrays 
because they're not collections" problem.  Viz, if we were dealing with 
an actual collection (as in Javascript, or many PHP libraries), we could 
do the following:

$ret = $scandir($arg)
   ->filter(function($x) { return $x !== '.' && $x != '..'; })
   ->map(function ($x) use ($arg) { return $arg . '/' . $x; })
   ->apply('getFileArg')
   ->merge($ret);

Which would be even nicer.  (This is also where the short-lambda 
proposal would come in useful.)  That's perfectly readable to me, and 
very clear what's happening.

My concern, if anything, is that |> feels more like a stop-gap hack 
around our clunky an uncomposable array handling rather than a solution 
to it.  I like the idea, I just wonder if we shouldn't fix the 
underlying problem and make functions better composable in the first place.

--Larry Garfield

---

## #92968 — Stanislav Malyshev — 2016-04-30T00:19:51+00:00

URL: https://externals.io/message/92960#92968 · In reply to: #92960

Hi!

> This is one of my favorites out of HackLang.  It's pure syntactic
> sugar, but it goes a long way towards improving readability.
> https://wiki.php.net/rfc/pipe-operator

I think this takes the syntax too much towards Perl 6 direction. There
is a lot of fun in inventing cool signs like |> and making them do
nontrivial cool stuff. There's much less fun when you try to figure out
what such code is actually doing - and especially why it is not doing
what you thought it is doing. Brian Kernigan once said:

"Everyone knows that debugging is twice as hard as writing a program in
the first place. So if you're as clever as you can be when you write it,
how will you ever debug it?"

I think this applies not only to debugging, but also to reading programs
in general. If we are "as clever as we can be" with syntax, the program
becomes unreadable.

For this feature specifically, I see nothing wrong with assigning
intermediate results to variables - moreover, I see an advantage to it
both in reading (you tell people what you expect to have happened in
this stage) and in debugging (you can actually *see* what happened and
*check* whether what you thought should be happening actually happened).

If we could achieve the elegance of Unix pipes, I'd agree that it is
simple enough that we can see what is going on - but having it rely on
two magic things which are in different part of expression and
context-dependent ($$ means different things in different places) is too
much for me.

-- 
Stas Malyshev
smalyshev@gmail.com

---

## #92970 — Marco Pivetta — 2016-04-30T06:49:31+00:00

URL: https://externals.io/message/92960#92970 · In reply to: #92968

I would say that this makes the entire functional approach:

 1. more readable
 2. easier to debug (woah, we can haz line numbers for failures!)

Here's the pseudo-code for a typical request/response dispatch cycle:

$request = getGlobals()
 |> parseRequest($$)
 |> buildPsr7Request($$);

$response = loadConfig()
 |> buildDic($$)
 |> getApp($$)
 |> getRouter($app)
 |> getDispatcher($$, $request)
 |> dispatchBusinessLogic($$, $request, new `Response()`)
 |> renderResponse($$)
 |> buildPsr7Response($$)
 |> emit($$);

Here's what it would look like with a separate approach (more
business-oriented):

buildRequest() // (basically the first part of the previous example here)
 |> validate($$)
 |> convertToCommand($$)
 |> execute($$)
 |> convertToViewModel($$)
 |> render($$)
 |> convertToHttpResponse($$)
 |> emit($$)

I think this is much more readable/clear than any event-driven or
procedural approach.
We know exactly what is going on, we will get clear stack traces (this
could need some fine-tuning of the current PR - I didn't try it out yet)
and we will be able to introduce intermediate steps with ease. In addition
to all that, this will ease reactive programming by a lot, as piping is a
natural fit for streams and all abstractions around streams.

The syntax is also already effective in F# (you can write very expressive
and easy to understand code with it) and now also landed in Hack.

I'd say +10 to this proposal :D



Marco Pivetta

http://twitter.com/Ocramius

http://ocramius.github.com/



> Hi!
>
> > […]
>
> I think this takes the syntax too much towards Perl 6 direction. There
> is a lot of fun in inventing cool signs like |> and making them do
> nontrivial cool stuff. There's much less fun when you try to figure out
> what such code is actually doing - and especially why it is not doing
> what you thought it is doing. Brian Kernigan once said:
>
> "Everyone knows that debugging is twice as hard as writing a program in
> the first place. So if you're as clever as you can be when you write it,
> how will you ever debug it?"
>
> I think this applies not only to debugging, but also to reading programs
> in general. If we are "as clever as we can be" with syntax, the program
> becomes unreadable.
>
> For this feature specifically, I see nothing wrong with assigning
> intermediate results to variables - moreover, I see an advantage to it
> both in reading (you tell people what you expect to have happened in
> this stage) and in debugging (you can actually *see* what happened and
> *check* whether what you thought should be happening actually happened).
>
> If we could achieve the elegance of Unix pipes, I'd agree that it is
> simple enough that we can see what is going on - but having it rely on
> two magic things which are in different part of expression and
> context-dependent ($$ means different things in different places) is too
> much for me.
>
> --
> Stas Malyshev
> smalyshev@gmail.com

---

## #92975 — Thomas Punt — 2016-04-30T10:00:01+00:00

URL: https://externals.io/message/92960#92975

Hi!

> From: ocramius@gmail.com
>
> I would say that this makes the entire functional approach:
>
> 1. more readable
> 2. easier to debug (woah, we can haz line numbers for failures!)
>
> Here's the pseudo-code for a typical request/response dispatch cycle:
>
> $request = getGlobals()
> |> parseRequest($$)
> |> buildPsr7Request($$);
>
> $response = loadConfig()
> |> buildDic($$)
> |> getApp($$)
> |> getRouter($app)
> |> getDispatcher($$, $request)
> |> dispatchBusinessLogic($$, $request, new `Response()`)
> |> renderResponse($$)
> |> buildPsr7Response($$)
> |> emit($$);
>
> Here's what it would look like with a separate approach (more
> business-oriented):
>
> buildRequest() // (basically the first part of the previous example here)
> |> validate($$)
> |> convertToCommand($$)
> |> execute($$)
> |> convertToViewModel($$)
> |> render($$)
> |> convertToHttpResponse($$)
> |> emit($$)
>
> I think this is much more readable/clear than any event-driven or
> procedural approach.
> We know exactly what is going on, we will get clear stack traces (this
> could need some fine-tuning of the current PR - I didn't try it out yet)
> and we will be able to introduce intermediate steps with ease. In addition
> to all that, this will ease reactive programming by a lot, as piping is a
> natural fit for streams and all abstractions around streams.
>
> The syntax is also already effective in F# (you can write very expressive
> and easy to understand code with it) and now also landed in Hack.
>
> I'd say +10 to this proposal :D

I have to agree with this. The examples in the RFC probably don't show the
best examples of it - the pipe operator really clarifies code when nesting
numerous simple function calls. Quoting the example above:

$response = loadConfig()
 |> buildDic($$)
 |> getApp($$)
 |> getRouter($app)
 |> getDispatcher($$, $request)
 |> dispatchBusinessLogic($$, $request, new `Response()`)
 |> renderResponse($$)
 |> buildPsr7Response($$)
 |> emit($$);

Without the pipe operator, it could look like the following:

$config = loadConfig();
$dic = buildDic(config);
$app = getApp($dic);
$router getRouter($app);
$dispatcher = getDispatcher($router, $request);
$businessResponse = dispatchBusinessLogic($dispatcher, $request, new `Response()`);
$renderedResponse = renderResponse($businessResponse);
$psr7Response = buildPsr7Response($renderedResponse);
$response = emit($psr7Response);

That's a lot of unnecessary assignments, however, and the variable names
don't even provide any additional readability benefits because the function
names are already self-documenting.

More likely, the above code would be written as numerous nested function
calls:

$dispatcher = getDispatcher(getRouter(getApp(buildDic(loadConfig()))));
$response = emit(buildPsr7Response(renderResponse(dispatchBusinessLogic($dispatcher, $request, new `Response()`))));

Having to dissect expressions like the above from inside-out is not a natural
way to read how the operations are actually occurring though. It's far easier
to read them from left-to-right or top-to-bottom, and this is where the
advantages of the pipe operator can really be seen.

So I'm definitely +1 on this feature - it's something I really miss from Elixir.

-Tom

---

## #92976 — Arvids Godjuks — 2016-04-30T10:02:06+00:00

URL: https://externals.io/message/92960#92976 · In reply to: #92970

I like the proposal, but readability should be on the forefront of things
that need to be done right. Yes, there are people that will make it an
unreadable mess, but we have code reformat, so if the syntax is verbose
enought to be read at a glance, I'm all for it.

+1 as long as syntax gets a hard look at and examples in RFC are amended to
show it better :)



> I would say that this makes the entire functional approach:
>
>  1. more readable
>  2. easier to debug (woah, we can haz line numbers for failures!)
>
> Here's the pseudo-code for a typical request/response dispatch cycle:
>
> $request = getGlobals()
>  |> parseRequest($$)
>  |> buildPsr7Request($$);
>
> $response = loadConfig()
>  |> buildDic($$)
>  |> getApp($$)
>  |> getRouter($app)
>  |> getDispatcher($$, $request)
>  |> dispatchBusinessLogic($$, $request, new `Response()`)
>  |> renderResponse($$)
>  |> buildPsr7Response($$)
>  |> emit($$);
>
> Here's what it would look like with a separate approach (more
> business-oriented):
>
> buildRequest() // (basically the first part of the previous example here)
>  |> validate($$)
>  |> convertToCommand($$)
>  |> execute($$)
>  |> convertToViewModel($$)
>  |> render($$)
>  |> convertToHttpResponse($$)
>  |> emit($$)
>
> I think this is much more readable/clear than any event-driven or
> procedural approach.
> We know exactly what is going on, we will get clear stack traces (this
> could need some fine-tuning of the current PR - I didn't try it out yet)
> and we will be able to introduce intermediate steps with ease. In addition
> to all that, this will ease reactive programming by a lot, as piping is a
> natural fit for streams and all abstractions around streams.
>
> The syntax is also already effective in F# (you can write very expressive
> and easy to understand code with it) and now also landed in Hack.
>
> I'd say +10 to this proposal :D
>
>
>
> Marco Pivetta
>
> http://twitter.com/Ocramius
>
> http://ocramius.github.com/
>
> 
>
> > […]

---

## #92982 — Lester Caine — 2016-04-30T13:52:36+00:00

URL: https://externals.io/message/92960#92982 · In reply to: #92960

> This is one of my favorites out of HackLang.  It's pure syntactic
> sugar, but it goes a long way towards improving readability.
> https://wiki.php.net/rfc/pipe-operator

I can see the attraction, but question it's usability in real time code.
It assumes everything has a single linear flow? Once one has branches
handing different results from the various stages it's usability looks
questionable. While one may well be able to break things down with
exceptions handling all of the invalid returns, if one needs to 'break
the flow' to put in an alternate path then what is supposedly a simple
sequence of steps becomes difficult to rework to allow for the change?
Exceptions should be reserved for out of the ordinary events, not
changes in work flow because different paths are equally valid?

-- 
Lester Caine - G8HFL
-----------------------------
Contact - http://lsces.co.uk/wiki/?page=contact
L.S.Caine Electronic Services - http://lsces.co.uk
EnquirySolve - http://enquirysolve.com/
Model Engineers Digital Workshop - http://medw.co.uk
Rainbow Digital Media - http://rainbowdigitalmedia.co.uk

---

## #92983 — Marco Pivetta — 2016-04-30T13:57:34+00:00

URL: https://externals.io/message/92960#92983 · In reply to: #92982

Relevant: https://youtu.be/UvD1VjRvGIk


> 
> > […]
>
> I can see the attraction, but question it's usability in real time code.
> It assumes everything has a single linear flow? Once one has branches
> handing different results from the various stages it's usability looks
> questionable. While one may well be able to break things down with
> exceptions handling all of the invalid returns, if one needs to 'break
> the flow' to put in an alternate path then what is supposedly a simple
> sequence of steps becomes difficult to rework to allow for the change?
> Exceptions should be reserved for out of the ordinary events, not
> changes in work flow because different paths are equally valid?
>
> --
> Lester Caine - G8HFL
> -----------------------------
> Contact - http://lsces.co.uk/wiki/?page=contact
> L.S.Caine Electronic Services - http://lsces.co.uk
> EnquirySolve - http://enquirysolve.com/
> Model Engineers Digital Workshop - http://medw.co.uk
> Rainbow Digital Media - http://rainbowdigitalmedia.co.uk

---

## #92984 — Paul Jones — 2016-04-30T14:40:11+00:00

URL: https://externals.io/message/92960#92984 · In reply to: #92960

> 
> 
> This is one of my favorites out of HackLang.  It's pure syntactic
> sugar, but it goes a long way towards improving readability.
> https://wiki.php.net/rfc/pipe-operator

I like this a lot.


-- 

Paul M. Jones
http://paul-m-jones.com

---

## #92985 — Lester Caine — 2016-04-30T14:43:03+00:00

URL: https://externals.io/message/92960#92985 · In reply to: #92983

> Relevant: https://youtu.be/UvD1VjRvGIk

Trimming the now useless error code ....

As I said in the message ... no problem if you simply have a SINGLE
pathway through the code. That video simply assumes that anything that
is not success is an error. MUCH of my code base has turntables or three
way switches which in addition to possible errors, you have a lot more
than one 'success' output.

The pipe operator does not provide a work flow as demonstrated in that
video ...

-- 
Lester Caine - G8HFL
-----------------------------
Contact - http://lsces.co.uk/wiki/?page=contact
L.S.Caine Electronic Services - http://lsces.co.uk
EnquirySolve - http://enquirysolve.com/
Model Engineers Digital Workshop - http://medw.co.uk
Rainbow Digital Media - http://rainbowdigitalmedia.co.uk

---

## #92986 — Peter Lind — 2016-04-30T14:50:31+00:00

URL: https://externals.io/message/92960#92986 · In reply to: #92985

>
> 
> > […]
>
> Trimming the now useless error code ....
>
> As I said in the message ... no problem if you simply have a SINGLE
> pathway through the code. That video simply assumes that anything that
> is not success is an error. MUCH of my code base has turntables or three
> way switches which in addition to possible errors, you have a lot more
> than one 'success' output.
>
> The pipe operator does not provide a work flow as demonstrated in that
> video ...

So in essence, any feature that doesn't fit your codebase is wrong. About
tight?

> --
> Lester Caine - G8HFL
> -----------------------------
> Contact - http://lsces.co.uk/wiki/?page=contact
> L.S.Caine Electronic Services - http://lsces.co.uk
> EnquirySolve - http://enquirysolve.com/
> Model Engineers Digital Workshop - http://medw.co.uk
> Rainbow Digital Media - http://rainbowdigitalmedia.co.uk

---

## #92987 — rich gray — 2016-04-30T15:03:21+00:00

URL: https://externals.io/message/92960#92987 · In reply to: #92985

> As I said in the message ... no problem if you simply have a SINGLE
> pathway through the code. That video simply assumes that anything that
> is not success is an error. MUCH of my code base has turntables or three
> way switches which in addition to possible errors, you have a lot more
> than one 'success' output.
>
>
If it doesn't suit your code base design then simply don't use the 
feature ... I don't see the need for you to raise objections here simply 
because it doesn't fit your purposes..

---

## #92988 — Fleshgrinder — 2016-04-30T15:05:11+00:00

URL: https://externals.io/message/92960#92988 · In reply to: #92970

> I think this is much more readable/clear than any event-driven or
> procedural approach.
> We know exactly what is going on, we will get clear stack traces (this
> could need some fine-tuning of the current PR - I didn't try it out yet)
> and we will be able to introduce intermediate steps with ease. In addition
> to all that, this will ease reactive programming by a lot, as piping is a
> natural fit for streams and all abstractions around streams.
> 
> The syntax is also already effective in F# (you can write very expressive
> and easy to understand code with it) and now also landed in Hack.
> 
> I'd say +10 to this proposal :D
> 

I am with Stanislav on this one or rather meh. It also goes against what
you usually advocate: there should be only one way to achieve things (I
remember your /fluent interface/ example very well). Unless you changed
your mind regarding that. Hence, it is nice syntactic sugar but it does
not allow us to solve problems that we cannot solve already.

-- 
Richard "Fleshgrinder" Fussenegger

---

## #92989 — Marco Pivetta — 2016-04-30T15:23:14+00:00

URL: https://externals.io/message/92960#92989 · In reply to: #92988

This feature has nothing to do with fluent interfaces, nor has their flaws.
The readability of piped processing for functional code is simply a
no-brainer here, and doesn't require any API changes in function signatures
either: it is not "a different way of doing the same thing".


> 
> > […]
> addition
> > […]
>
> I am with Stanislav on this one or rather meh. It also goes against what
> you usually advocate: there should be only one way to achieve things (I
> remember your /fluent interface/ example very well). Unless you changed
> your mind regarding that. Hence, it is nice syntactic sugar but it does
> not allow us to solve problems that we cannot solve already.
>
> --
> Richard "Fleshgrinder" Fussenegger

---

## #92990 — Fleshgrinder — 2016-04-30T15:51:44+00:00

URL: https://externals.io/message/92960#92990 · In reply to: #92989

> This feature has nothing to do with fluent interfaces, nor has their flaws.
> The readability of piped processing for functional code is simply a
> no-brainer here, and doesn't require any API changes in function signatures
> either: it is not "a different way of doing the same thing".
> 
> 

This is the example code from the RFC written in a normal procedural way.

$files = scandir($arg);
$files = array_filter($files, function ($x) {
  return $x !== '.' && $x !== '..';
});
$files = array_map(function ($x) use ($arg) {
  return $path . '/' . $x;
}, $files);
$ret = array_merge($ret, getFileArg($files));

This is the example code with the pipe operator.

$ret = scandir($arg)
  |> array_filter($$, function($x) { return $x !== '.' && $x != '..'; })
  |> array_map(function ($x) use ($arg) { return $arg . '/' . $x; }, $$)
  |> getFileArg($$)
  |> array_merge($ret, $$);

Definitely looks like "a different way of doing the same thing" to me.
So does the initial super ugly example code of nesting.

Again, I am not really against the proposal nor do I argue against the
fact that it improves readability of certain code constructs. I am just
meh on it because such code can pretty much always be rewritten in a way
that makes it more readable, easier to debug, and even faster. In the
example code we have:

array_filter with O(n)

array_map with O(n)

array_merge with O(∑ array_i, i != 1) and in our case O(n) where n
equals the total count of elements in $files/$$.

In my universe `getFileArg` (note the absence of a plural form) would
operate on a single path and not on an array of paths:

foreach (new DirectoryIterator($arg) as $path) {
  if ($path->isDot() === false) {
    $ret[] = getFileArg($arg . `DIRECTORY_SEPARATOR` . $path);
  }
}

I cannot tell if this construct is really equivalent to the example from
the RFC (because I do not know what $ret contains) but it should do
exactly the same. It is more readable, easier to debug, and much faster.

Now you may argue that the example is simply flawed. Well, maybe, but
functional programming is nice if you have a language that is
functional. The only functional part in PHP are callbacks, everything
else is just plain old procedural + OO and importing more functional
concepts will not change that. However, I am not saying that every
functional concept makes no sense just that the pipes are not very
appealing to me because I only see "a different way of doing the same
thing" with slower code. :P

-- 
Richard "Fleshgrinder" Fussenegger

---

## #92991 — Fleshgrinder — 2016-04-30T15:55:58+00:00

URL: https://externals.io/message/92960#92991 · In reply to: #92990

> $ret = scandir($arg)
>   |> array_filter($$, function($x) { return $x !== '.' && $x != '..'; })
>   |> array_map(function ($x) use ($arg) { return $arg . '/' . $x; }, $$)
>   |> getFileArg($$)
>   |> array_merge($ret, $$);

This should actually be formatted as to be fair contender to the
procedural example:

$ret = scandir($arg)
  |> array_filter($$, function ($x) {
    return $x !== '.' && $x !== '..';
  })
  }> array_map(function ($x) use ($arg) {
    return $arg . '/' . $x;
  }, $$)
  |> getFileArg($$)
  |> array_merge($ret, $$);

Not a big difference now...

$files = scandir($arg);
$files = array_filter($files, function ($x) {
  return $x !== '.' && $x !== '..';
});
$files = array_map(function ($x) use ($arg) {
  return $path . '/' . $x;
}, $files);
$ret = array_merge($ret, getFileArg($files));

...the only thing that bugs me in the procedural code is the fact that
array_filter and array_map take their arguments in different order. :P

-- 
Richard "Fleshgrinder" Fussenegger

---

## #92992 — Lester Caine — 2016-04-30T16:06:59+00:00

URL: https://externals.io/message/92960#92992 · In reply to: #92987

> > […]
> If it doesn't suit your code base design then simply don't use the
> feature ... I don't see the need for you to raise objections here simply
> because it doesn't fit your purposes..

The problem is not with ME using this sort of re-factoring of code, it's
managing and maintaining code others have 'improved' but applying it. I
have a similar problem with chained operators in classes like PDO.
Invariably the 'edge cases' where nothing is returned at a point in the
chain are not handled or throw an exception, where ACTUALLY you want to
process the 'mid point' of the chain and create a split in the flow.
Someone having tidied what was perhaps nicely documented and structured
code into the one single chain may not be the right thing when missing
elements need to be added to the workflow.

Having now watched more of the linked talk I find the one thing that I
do like with that model ... trap exceptions and put them back into the
workflow ... The second half of the video is well worth watching! I like
the idea that you keep the action open to do retries such as resending
the email if it failed first time. That is not a simple exception on the
workflow of a pipe operation but is a major difference between compiled
languages and scripting process.

-- 
Lester Caine - G8HFL
-----------------------------
Contact - http://lsces.co.uk/wiki/?page=contact
L.S.Caine Electronic Services - http://lsces.co.uk
EnquirySolve - http://enquirysolve.com/
Model Engineers Digital Workshop - http://medw.co.uk
Rainbow Digital Media - http://rainbowdigitalmedia.co.uk

---

## #92996 — Larry Garfield — 2016-04-30T18:05:44+00:00

URL: https://externals.io/message/92960#92996 · In reply to: #92968

> Hi!
>
> > […]
> I think this takes the syntax too much towards Perl 6 direction. There
> is a lot of fun in inventing cool signs like |> and making them do
> nontrivial cool stuff. There's much less fun when you try to figure out
> what such code is actually doing - and especially why it is not doing
> what you thought it is doing. Brian Kernigan once said:
>
> "Everyone knows that debugging is twice as hard as writing a program in
> the first place. So if you're as clever as you can be when you write it,
> how will you ever debug it?"
>
> I think this applies not only to debugging, but also to reading programs
> in general. If we are "as clever as we can be" with syntax, the program
> becomes unreadable.
>
> For this feature specifically, I see nothing wrong with assigning
> intermediate results to variables - moreover, I see an advantage to it
> both in reading (you tell people what you expect to have happened in
> this stage) and in debugging (you can actually *see* what happened and
> *check* whether what you thought should be happening actually happened).
>
> If we could achieve the elegance of Unix pipes, I'd agree that it is
> simple enough that we can see what is going on - but having it rely on
> two magic things which are in different part of expression and
> context-dependent ($$ means different things in different places) is too
> much for me.

See, I take that quote in the exact opposite direction.  I find chaining 
methods to be far, far less "clever" than deeply nesting them inside 
each other.  We shouldn't force pointless intermediate variables on 
people for the sake of readability.  The fair comparison is the deeply 
nested version vs. the chained version, and in that comparison the 
readability of the chained version is far, far better.

Lester pointed out error handling and branching.  True, the pipe 
chaining wouldn't handle that, but it doesn't claim to.  Neither does 
directly nesting the function calls inside each other.  If you do need 
to pause, examine a value, and branch, then yes that's where you want an 
intermediate variable to analyze and nothing has changed.  However, 
passing an array to `array_filter()` then passing the result to 
 `array_map()` is always type safe (because array_filter still returns an 
array even if it filters down to 0 items), and array_map on an empty 
array is essentially a no-op, so I'm comfortable doing so, and wish I 
could do so more often with less fugly syntax. :-)

In a way... Sara, this syntax feels like it's only one step removed from 
promises; if any of the chained steps involve IO, it becomes basically 
what promises are for.  Am I barking down the wrong tree, or is there 
something we could connect there?  (I don't mean "reject this in favor 
of promises", but more "is there something we can do here to be forward 
thinking, since lots of people want to see async in core PHP?")

--Larry Garfield

---

## #92997 — lester@lsces.co.uk — 2016-04-30T18:35:38+00:00

URL: https://externals.io/message/92960#92997 · In reply to: #92996

However, 
passing an array to `array_filter()` then passing the result to  `array_map()` is always type safe (because array_filter still returns an 
array even if it filters down to 0 items), and array_map on an empty 
array is essentially a no-op, so I'm comfortable doing so, and wish I 
could do so more often with less fugly syntax. :-)

---

## #92998 — lester@lsces.co.uk — 2016-04-30T18:41:29+00:00

URL: https://externals.io/message/92960#92998 · In reply to: #92997

God I hate crap software ... TRYING to reply without top post ... but Samsung is incapable!

What I was trying to comment on was that trapping that no result is returned may be what is needed, so handling the empty array rather than a null return just means having to remember what you do use :(

Sent from my android device so quoting is crap ... need to kill these painful email clients!

-----Original Message-----
From: lester@lsces.co.uk
To: internals@lists.php.net, Larry Garfield <larry@garfieldtech.com>
Sent: Sat, 30 Apr 2016 19:35
Subject: Re: [PHP-DEV] [RFC] Pipe Operator

However, 
passing an array to `array_filter()` then passing the result to  `array_map()` is always type safe (because array_filter still returns an 
array even if it filters down to 0 items), and array_map on an empty 
array is essentially a no-op, so I'm comfortable doing so, and wish I 
could do so more often with less fugly syntax. :-)

---

## #92999 — Sara Golemon — 2016-04-30T18:57:40+00:00

URL: https://externals.io/message/92960#92999 · In reply to: #92998

> God I hate crap software ... TRYING to reply without top post ... but Samsung is incapable!
>
> What I was trying to comment on was that trapping that no result is returned may be what is needed, so handling the empty array rather than a null return just means having to remember what you do use :(
>
What follows is a terrible idea and I don't mean to propose it as a
solution, but to spark further conversation:

$result = getData()
  ?> doStuffWith($$)
  :> actOnFailureOf($$);

Basically, a chaining ternary such that the result of the condition
expression propagates to the true/false expressions.  Not really the
same feature at this point, but a similar chaining-friendly take on
the standard ternary.  Just thinking out loud...

-Sara

---

## #93000 — Stanislav Malyshev — 2016-04-30T19:43:48+00:00

URL: https://externals.io/message/92960#93000 · In reply to: #92996

Hi!

> See, I take that quote in the exact opposite direction.  I find chaining
> methods to be far, far less "clever" than deeply nesting them inside

If you tell me that syntax like "foo() |> bar($$)" is more natural or
intuitive or readily understandable to anyone with any programming and
PHP background than "$fooResult = foo(); bar($fooResult); " then we
indeed have exactly opposite understanding of what is natural and intuitive.

I think that overly clever is inventing cryptic syntax to save a couple
of keystrokes and rearrange code in unusual pattern that looks unlike
the code used to look before and resembles some other language (this
time it's F#? how many languages PHP should be at once - can we get a
dozen?)

> each other.  We shouldn't force pointless intermediate variables on

Why you think they are pointless? And you do produce them, you just hide
them behind $$ so you can not actually neither check them nor understand
which value goes where without tracing through the code with your finger.

> people for the sake of readability.  The fair comparison is the deeply
> nested version vs. the chained version, and in that comparison the
> readability of the chained version is far, far better.

No, it's not a fair comparison. You shouldn't do deep nesting, and you
shouldn't do cryptic syntax either. Variables are not evil. They are
there to make things easier for you. Use them.

> intermediate variable to analyze and nothing has changed.  However,
> passing an array to `array_filter()` then passing the result to
> `array_map()` is always type safe (because array_filter still returns an
> array even if it filters down to 0 items), and array_map on an empty
> array is essentially a no-op, so I'm comfortable doing so, and wish I
> could do so more often with less fugly syntax. :-)

I appreciate the careful choice of the example on RFC - indeed, in some
very carefully chosen cases (mostly with arrays) you could get away with
chaining without getting into trouble. In most cases where it would be
used, though, that would mean not checking for errors, forgetting corner
cases and so on. And that's how people would use this, because none of
the existing functions were developed to work with this pattern, so you
can only get away with it due to luck.

In strictly typed languages like Haskell, they use types like Option and
Maybe to enable this pattern - essentially, to enable functions to
operate on more than one simple type of value. However, it doesn't look
in place in PHP, and it would require much more deep restructuring to
actually make it work than just having |> as an operator.

> of promises", but more "is there something we can do here to be forward
> thinking, since lots of people want to see async in core PHP?")

I'm not sure what promises have to do with inventing strange syntax to
pass result of one function as a parameter to another.
-- 
Stas Malyshev
smalyshev@gmail.com

---

## #93001 — Stanislav Malyshev — 2016-04-30T19:43:58+00:00

URL: https://externals.io/message/92960#93001 · In reply to: #92996

Hi!

> See, I take that quote in the exact opposite direction.  I find chaining
> methods to be far, far less "clever" than deeply nesting them inside

If you tell me that syntax like "foo() |> bar($$)" is more natural or
intuitive or readily understandable to anyone with any programming and
PHP background than "$fooResult = foo(); bar($fooResult); " then we
indeed have exactly opposite understanding of what is natural and intuitive.

I think that overly clever is inventing cryptic syntax to save a couple
of keystrokes and rearrange code in unusual pattern that looks unlike
the code used to look before and resembles some other language (this
time it's F#? how many languages PHP should be at once - can we get a
dozen?)

> each other.  We shouldn't force pointless intermediate variables on

Why you think they are pointless? And you do produce them, you just hide
them behind $$ so you can not actually neither check them nor understand
which value goes where without tracing through the code with your finger.

> people for the sake of readability.  The fair comparison is the deeply
> nested version vs. the chained version, and in that comparison the
> readability of the chained version is far, far better.

No, it's not a fair comparison. You shouldn't do deep nesting, and you
shouldn't do cryptic syntax either. Variables are not evil. They are
there to make things easier for you. Use them.

> intermediate variable to analyze and nothing has changed.  However,
> passing an array to `array_filter()` then passing the result to
> `array_map()` is always type safe (because array_filter still returns an
> array even if it filters down to 0 items), and array_map on an empty
> array is essentially a no-op, so I'm comfortable doing so, and wish I
> could do so more often with less fugly syntax. :-)

I appreciate the careful choice of the example on RFC - indeed, in some
very carefully chosen cases (mostly with arrays) you could get away with
chaining without getting into trouble. In most cases where it would be
used, though, that would mean not checking for errors, forgetting corner
cases and so on. And that's how people would use this, because none of
the existing functions were developed to work with this pattern, so you
can only get away with it due to luck.

In strictly typed languages like Haskell, they use types like Option and
Maybe to enable this pattern - essentially, to enable functions to
operate on more than one simple type of value. However, it doesn't look
in place in PHP, and it would require much more deep restructuring to
actually make it work than just having |> as an operator.

> of promises", but more "is there something we can do here to be forward
> thinking, since lots of people want to see async in core PHP?")

I'm not sure what promises have to do with inventing strange syntax to
pass result of one function as a parameter to another.
-- 
Stas Malyshev
smalyshev@gmail.com

---

Next page: https://externals.io/message/92960.md?page=2
