Remix.run Logo
qwertox 8 days ago

I don't see how this is hard to reason about, assuming this is the resulting code when using variables:

  $tags        = ...array_column($arr, 'tags');
  $merged_tags = array_merge($tags);
  $unique_tags = array_unique($merged_tags);
  $tag_values  = array_values($unique_tags);
It also makes it easier to inspect the values after each step.
jeroenhd 8 days ago | parent | next [-]

Correctly naming things is one of the harder challenges in computer programming. Putting effort into naming intermediates that you're going to throw out is a waste. Plus, the more variables you introduce, the more likely you'll accidentally re-use a variable name somewhere down the line.

With PHP allowing variable initialization in one branch but not the other, and continuing execution by default when an undeclared variable is passed, declaring more variables can lead to an annoying class of bugs that would require significant (breaking) changes to the core language to completely eliminate.

lawn 7 days ago | parent | prev | next [-]

Introducing a new variable every single line adda a bunch of cognitive load compared to the pipe operator.

It's much easier skim with the pipe operator and it's more robust too (for example reordering is a pain with variables, it's easy to introduce errors).

agos 8 days ago | parent | prev | next [-]

I don't think inspecting this is easier than adding |> IO.inspect() to a pipe chain

harg 7 days ago | parent | next [-]

Or putting `|> dbg()` at the end and let it print the value at every step of the chain

wraptile 7 days ago | parent | prev [-]

modifying code just to attach a breakpoint is kinda silly in this day and age.

const_cast 7 days ago | parent | prev | next [-]

The main problem with this approach, as someone who programs in PHP daily, is it pollutes the scope. That makes debugging much, much harder - you lose track of variables, and the current state of the program is complicated. IMO, if a value is a throwaway, like an intermediate, we shouldn't be able to use it. So method chaining or nesting function calls prevents them. Then, when we break after these functions, we can't see fake values. It also prevents someone in the future mutating the throwaway values. Someone could easily insert logic or a call that mutates something in the middle of this and breaks the chain.

One way this is prevented in PHP is just using functions. But then you have functions just for the sake of scope, which isn't really what they're for. That introduces other annoyances.

cess11 8 days ago | parent | prev | next [-]

Such variable threading tends to be harder to skim through in production code, the intermediates become noise that's harder to filter out than a repeated symbol like |>.

Preferably you should also be sure that the functions are compatible with the data type going in and only rarely have to break it to dump data mid-chain. If you expect that kind of erroring it's likely a builder-chain with -> is a better alternative and do logging in the methods.

kristopolous 7 days ago | parent | prev | next [-]

It's easier to write, copy paste, compose and comment

r34 8 days ago | parent | prev [-]

Your version includes 4 variables. Pipes don't create those intermediate variables, so they are more memory efficient.

Readability is mostly matter of habit. One reads easily what he/she is used to read.

qwertox 8 days ago | parent | next [-]

It's true that pipes are more readable, and for many cases they will be the better option, but the example of nested functions just doesn't hold.

That's like saying someone would use this:

   $result = $arr |> fn($x) => array_column($x, 'tags') |> fn($x) => array_merge(...$x) |> array_unique(...) |> array_values(...)
which is harder to reason about than the nested functions.

   array_values( array_unique( array_merge( ...array_column($arr, 'tags') ) ) );
or

   array_values(
     array_unique(
       array_merge(
         ...array_column($arr, 'tags')
       )
     )
   );
navalino 8 days ago | parent | next [-]

It is more readable and better option — you have to parse it from the innermost function to the outermost just to understand what it's doing. With the pipe, it's more straightforward: you read it step by step — do this, then that, then the next — just like how you'd naturally read instructions.

troupo 7 days ago | parent | prev | next [-]

Why didn't you format the pipes, too?

  $result = $arr
    |> fn($x) => array_column($x, 'tags')
    |> fn($x) => array_merge(...$x)
    |> array_unique(...)
    |> array_values(...)
vs

   array_values(
     array_unique(
       array_merge(
         ...array_column($arr, 'tags')
       )
     )
   );
With pipes you have linear sequence of data transformations. With nested function calls you have to start with innermost function and proceed all the way top the outermost layer.
qwertox 6 days ago | parent [-]

Because they were already formatted that way to begin with.

account42 7 days ago | parent | prev [-]

The pipe syntax is much more readable than nested function calls when you need additional arguments for intermediate functions. With nested functions it becomes hard to see which functions those arguments belong to even if you try to help it with formatting.

girvo 8 days ago | parent | prev [-]

> so they are more memory efficient

They can be. It depends on the language, interpreter, compiler, and whether you do anything with those intermediate variables and the optimiser can get rid of them.

r34 8 days ago | parent [-]

I thought we are talking about PHP8.5:)

girvo 7 days ago | parent [-]

Ah, I thought we were talking more generally about PL constructs that let you avoid intermediate variables, apologies :)