Live data from Hacker News

Pipe Operator (|>) For JavaScript

github.com

151–160 of 437 posts

Re: Pipe Operator (|>) For JavaScript

#151

Earlier quoted context omitted.

Nope, it becomes: g() |> f(%) |> d(e(), %) |> a(b(),c(), %) Which makes super clear what processing is actually done. Which is the data to process and which are just parameters of processing. Because it could equivalently be: e() |> d(%,f(g())) |> a(b(),c(), %) If the data you process is rather produced by e() not by g(). This new syntax allows you to express intent beyond what's possible without it.

If i saw that in a colleague's code, i'd be angry at them as that's not legible.

It's no less legible than original code and at least expresses the intent of what's being processed, what are the processing steps and what are processing parameters.

a(b(),c(),d(e(),f(g()))) is just function call soup.

Re: Pipe Operator (|>) For JavaScript

#152
post #128

Earlier quoted context omitted.

The F# syntax looks/acts a lot better here (especially paired with lodash). I also feel your code example wasn't done in the way people would actually use pipes. import {map, join} from 'lodash/fp' //iterators have better performance envars |> Object.entries |> map(([key, val]) => `${key}=${val}`) |> join(' ') |> x => chalk.dim('$ ' + x, 'node', join(' ', args)) |> console.log Even without lodash, it's still easy to…

The F# syntax would endlessly confuse me though. I'd always wonder whether |> join(' ') means join(x, ' ') or join(' ', x).

in F# syntax the right side is always a unary expression, so `|> join(' ')` means `|> join(' ')(x)`

(the requirement being that join(' ') returns a function that takes one arg)

Re: Pipe Operator (|>) For JavaScript

#153

I don't understand why you can't just use temporary variables. The article mentions mutation is bad, but what actually happens is that the name gets reassigned. No value is mutated. That brings me to something I really want in JS, actual unmutable values. If you use `const x = new SomeClass()`, you cannot reassign it, but you can change fields. The first time I encountered `const`, I thought it did the opposite. It w…

A problem is that you can only declare intermediate constants in a statement context, not an expression context. And with React, more and more JS devs are spending time in expression contexts Example: return ( {foo(bar(stuff))} ) There's no way to break out inline intermediate constants here; you have to bail out and do it up above the `return`. In this case that may not be too bad, but when you've got a hundred line…

But that's a symptom of issues with React, not issues with JavaScript.

React's declarative model makes it easy to write unreadable spaghetti React declarations that are nested ten levels deep. Nobody should be adding features to JavaScript to encourage that.

("Please, I'm begging you, for the love of sanity... Refactor into more than one component. Just one time. Look, functional components even make that cheap and easy now. Please please please, just take some of that nesting and put it in a new component.")

Re: Pipe Operator (|>) For JavaScript

#154

Earlier quoted context omitted.

The Hack proposal is horrible imo because it doesn’t look like JS anymore. The F# proposal is 99% of the benefit whilst being actually approachable.

I think we should have BOTH. Use |> for the Hack proposal and -> for F# -style.

I'd far rather save that for an alternative switch expression with pattern matching.

    const slowSum = (lst: {x: number}[]) =>
      switch(list) {
        [{x}, ...y]   -> x + slowSum(y) //not tail recursive
        [{x}, {x: y}] -> x + y          //alias second x to y
        [{x}]         -> x              //handle length 1
        []            -> 0              //handle length 0
      }

Re: Pipe Operator (|>) For JavaScript

#155
post #53

Temporary variables are often tedious? I have found that well named temporary variables are the only clear way to comment code without actually writing the comment. The version with temporary variables is much easier to understand without having to read the rest of the code.

All I can figure is the people who keep pushing this sort of stuff in JS have very different problems than I do, if they think this will improve things rather than making them worse.

... I further suspect that their problems are mostly self-inflicted, but maybe I'm wrong about that.

Re: Pipe Operator (|>) For JavaScript

#156
post #27

Is this: Object.keys(envars) .map(envar => `${envar}=${envars[envar]}`) .join(' ') |> `$ ${%}` |> chalk.dim(%, 'node', args.join(' ')) |> console.log(%); Really better than: console.log(chalk.dim( `$ ${Object.keys(envars) .map(envar => `${envar}=${envars[envar]}`) .join(' ') }`, 'node', args.join(' ') )); That's the real-world example they have (I reformatted the second one slightly, because it looks better to me). N…

All of the examples look unreadable and hard to debug to me. Why not something like this rather than one massive nested instruction?

    let keys = Object.keys(envars);
    let text = keys.map(envar => `${envar}=${envars[envar]}`).join(" ");
    console.log(chalk.dim(`$ ${text}`), "node", args.join(' '));
That way you can easily inspect and verify the intermediate values at runtime. Helpful for you to see if your code works as expected, helpful for others to see what the code is doing.

Re: Pipe Operator (|>) For JavaScript

#157
post #53

Temporary variables are often tedious? I have found that well named temporary variables are the only clear way to comment code without actually writing the comment. The version with temporary variables is much easier to understand without having to read the rest of the code.

When there are a few they can be really great. But if you need to accurately name every single intermediate thing they can become visual noise that hides what happens.

In general, I think that when one does that, the code smell one is smelling isn't "This language isn't expressive enough; I need a third way to describe calling a function." It's "What I'm doing is actually complicated and I need to switch to describing it with a DSL, not adding more layers of frosting on this three-layer cake."

Re: Pipe Operator (|>) For JavaScript

#158
post #3

They should have stuck with the F# proposal. The hack proposal just takes one more giant step toward turning JS into Perl. Hack proposal value |> foo(%) for unary function calls, value |> foo(1, %) for n-ary function calls, value |> %.foo() for method calls, value |> % + 1 for arithmetic, value |> [%, 0] for array literals, value |> {foo: %} for object literals, value |> `${%}` for template literals, value |> new Foo…

The F# syntax looks more consistent with idiomatic JS.

It always seems there’s some obscure edge case that derails the nice path for these spec proposals though. I haven’t tracked the conversation on this one, but wonder why they didn’t go with it.

Re: Pipe Operator (|>) For JavaScript

#159

Earlier quoted context omitted.

If i saw that in a colleague's code, i'd be angry at them as that's not legible.

It's no less legible than original code and at least expresses the intent of what's being processed, what are the processing steps and what are processing parameters. a(b(),c(),d(e(),f(g()))) is just function call soup.

At least I can instantly tell which call is ultimately returning something, in that version.

Re: Pipe Operator (|>) For JavaScript

#160

Earlier quoted context omitted.

It's annoying to have to decide on and write out so many names. The intermediary names are not relevant to solving the problem. This is so much less noisy: value |> one |> two |> three

The intermediary names are extremely relevant to the next poor sucker who has to understand what you were trying to do. Code is read far more than it is written. Use temporary variables. Put in the effort to name them once, and then that effort pays back every time anyone needs to read and understand the code.

> The intermediary names are extremely relevant to the next poor sucker who has to understand what you were trying to do.

I just don't think that this is always true.

Consider:

    const highestScore = 
      players
      |> filter(x => x.isAlive)
      |> map(x => x.score)
      |> tryMax
I don't see how this is better:

    const alivePlayers = filter(x => x.isAlive)(players);
    const scoresOfalivePlayers = map(x => x.score)(alivePlayers);
    const highestScore = tryMax(scoresOfalivePlayers);
And you can add helpful comments to pipeline code if needed:

    const highestScore = 
      players
      |> filter(x => x.isAlive) // Dead players cannot win
      |> map(x => x.score)
      |> tryMax
More generally though, I don't see why forcing everyone to write out intermediary names all of the time leads to more readable code. If it's more readable to do so, I will. If a pipeline is more readable, why should we be prevented from using it?
Post reply on HN