31.01.2026 20:45, Rob Landers:
I get I could just ... not do that ... but since this seems to be about cognitive load, I think this would actually add additional cognitive load instead of removing it. Often, you need to find where the function returns; more often than you need to read actual pipes.
03.02.2026 22:13, Tim Düsterhus:
That said, I agree with the others that I find the proposal a step backwards in terms of readability. Having the `return` at the start of the line is a clear indicator that the function ends there and it is only a “single bit” of information I have to keep in mind while I continue reading until I find the semicolon ending the statement. It is also consistent with how assignments - the other primary use case for pipes - start with the target variable and the equals before the “pipe expression”. I think it would not be wrong to think of `return` as “assigning to the return value”.


Thanks for these comments. I find them among the most valuable in this discussion. They really made me think for a long time and experiment a lot.

To discuss them, I've been going through and trying to come up with different code options. Unfortunately, there aren't any successful examples yet in the real projects and libraries I work with. If you could provide links to repositories where pipe operator is actively used, I'd be very grateful. So for now I apologize for the fictitious example.

Let's take this complex function

    function process_request($request)
    {
        if ($request instanceof ManageEmployeeRequest) {
            if ($request instanceof CreateEmployeeRequest) {
                return $request |> logRequest(...) |> validateCreateEmployeeRequest(...) |> processCreateEmployee(...) |> formatEmployeeDataAsArray(...) |> convertArrayToJsonResponse(...);
            } elseif ($request instanceof UpdateEmployeeRequest) {
                return $request |> logRequest(...) |> validateUpdateEmployeeRequest(...) |> processUpdateEmployee(...) |> formatEmployeeDataAsArray(...) |> convertArrayToJsonResponse(...);
            } elseif ($request instanceof DeleteEmployeeRequest) {
                $request |> logRequest(...) |> validateDeleteEmployeeRequest(...) |> processDeleteEmployee(...);
                return [] |> convertArrayToJsonResponse(...);
            } else {
                throw new InvalidArgumentException("Unknown client request type");
            }
        }
        if ($request instanceof ManageClientRequest) {
            if ($request instanceof CreateClientRequest) {
                return $request |> validateCreateClientRequest(...) |> processCreateClient(...) |> formatClientDataAsArray(...) |> convertArrayToJsonResponse(...);
            } elseif ($request instanceof UpdateClientRequest) {
                return $request |> validateUpdateClientRequest(...) |> processUpdateClient(...) |> formatClientDataAsArray(...) |> convertArrayToJsonResponse(...);
            } elseif ($request instanceof DeleteClientRequest) {
                $request |> validateDeleteClientRequest(...) |> processDeleteClient(...);
                return convertArrayToJsonResponse([]);
            } else {
                throw new InvalidArgumentException("Unknown client request type");
            }
        }
        throw new InvalidArgumentException("Unknown request type");
    }

When we read this function, we are actually more interested in the structure of conditions and returns than in the actual actions. In this case, of course, return at the beginning of the line is indeed more readable. Here I completely agree with you.

But in real life we ​​won't use such long lines and the chain will be split into several lines. Here I used different code styles in one block for clarity.

    function process_request($request)
    {
        if ($request instanceof ManageEmployeeRequest) {
            if ($request instanceof CreateEmployeeRequest) {
                $request
                    |> logRequest(...)
                    |> validateCreateEmployeeRequest(...)
                    |> processCreateEmployee(...)
                    |> formatEmployeeDataAsArray(...)
                    |> convertArrayToJsonResponse(...)
                    |> return;
            } elseif ($request instanceof UpdateEmployeeRequest) {
                return
                    $request
                        |> logRequest(...)
                        |> validateUpdateEmployeeRequest(...)
                        |> processUpdateEmployee(...)
                        |> formatEmployeeDataAsArray(...)
                        |> convertArrayToJsonResponse(...);
            } elseif ($request instanceof DeleteEmployeeRequest) {
                $loggedRequest = logRequest($loggedRequest);
                $validatedRequest = validateDeleteEmployeeRequest($loggedRequest);
                processDeleteEmployee($validatedRequest);
                $response = convertArrayToJsonResponse([]);
                return $response;
            } else {
                throw new InvalidArgumentException("Unknown client request type");
            }
        }
        // ...
    }

In my case, the IDE brightly highlights 'if', 'elseif', and 'return' keywords. In this version, I clearly see the return in the first two blocks, despite its different placement. The 'return' in the third block stands out the least; it blends in with the preceding text because there is no indentation. But when I'm composing a letter, I don't have syntax highlighting, but the feeling is the same.

The third block looks more like a chain of actions. Of course, it's written in imperative style.

The first block also looks like a chain of actions. Vision very quickly stops paying attention to `|>`. Moreover, it fits seamlessly into the conditions structure. It's clear that this chain of transformations is executed under these very conditions. And here it seems to me very fortunate that after reading the block to the end, we see return, and we don’t need to look up 6 lines above to remember why we performed all these actions.

I myself reread the second block several times, trying to understand whether it really is more convenient to read. But every time I read the chain of actions to the end, I caught myself thinking that I didn’t want to go back to check if there was a ‘return’ there. On the contrary, during the time I was working with this example, my brain developed a rule, and seeing a vertical chain of '|>', I immediately began to look at the end of the block to understand whether there is a return at the end. It's like looking at the first and last lines of a third block, skipping over everything in between. But with `|>`, your eye kind of slides down them, and you get to `|> return;` much faster.

I also noticed that when I look at the second block, my vision selects the block starting with `$request`. For some reason it crops the `return` located to the left and above. I think this is because it is a keyword that has ceased to carry meaning for us, and we perceive it as a punctuation mark, rather than as carrying meaning.

Reply via email to