RE: [PHP-DEV] [RFC] Pipe Operator
| From: | Thomas Punt | Date: | Sat, 30 Apr 2016 10:00:01 +0000 |
| Subject: | RE: [PHP-DEV] [RFC] Pipe Operator | ||
| References: | 1 2 3 | Groups: | php.internals |
| Request: | Send a blank email to internals+get-92975@lists.php.net to get a copy of this message | ||
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