Live data from Hacker News

Pipe Operator (|>) For JavaScript

github.com

171–180 of 437 posts

Re: Pipe Operator (|>) For JavaScript

#171
That % syntax is just completely unlike anything else I have seen in JS.

As a multi paradigm language, JS typically suffers from whatever programming style is on trend when these features are implemented. We are apparently on the other side of the pendulum now, but I can’t remember the last time I worked with a class and felt like that was right either.

Re: Pipe Operator (|>) For JavaScript

#172
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.

This. Temporary variables are the way to go for deconstructing a complex expression like this. Everything is more readable when you put the results of an expression with two to four terms in a well-named variable. Trying to put everything into one giant closed-form expression feels clever and smart, but it's really just getting in the way of the next poor sucker who needs to understand what you were doing. This works…

Temporary variable make debugging easier too.

Re: Pipe Operator (|>) For JavaScript

#173
Cool stuff, but I miss the "it" variable from HyperTalk (the language used by HyperCard) which contained the result of prompts to the user. Just search for "The it variable":

http://www.jaedworks.com/hypercard/HT-Masters/scripting.html

  ask "How many minutes do you want to play?"
  put it * 60 into timeToPlay  -- convert it into seconds
Today we could have a reserved keyword that holds the result of the last statement executed. A practical example adapted from the article using "it" might look like:

  Object.keys(envars)
  it.map(envar => `${envar}=${envars[envar]}`)
  it.join(' ')
  `$ ${it}`
  chalk.dim(it, 'node', args.join(' '))
  console.log(it);
A better name for "it" today might be "_", "result" or perhaps '$' in a shell-inspired language like php. Most shells support "$?" so that could work too:

  # prints 0
  true ; echo $?
  
  # prints 1
  false ; echo $?

Re: Pipe Operator (|>) For JavaScript

#174
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.

I struggle to think of real-world examples where I've just needed to chain and chain and chain values of different types more than a handful of times. The claimed need for the pipe operator is this construction:

    function bakeCake() {
      return separateFromPan(coolOff(bake(pour(mix(gatherIngredients(), bowl), pan), 350, 45), 30));
    }
The piped code looks like:

    function bakeCake() {
      return gatherIngredients()
        |> mix(%, bowl)
        |> pour(%, pan)
        |> bake(%, 350, 45)
        |> coolOff(%)
        |> separateFromPan(%)
       ;
Which is... fine? It certainly looks better than the mess we started with, but adding names here only helps clarify each step.

    function bakeCake() {
      const ingredients = gatherIngredients();
      const batter = mix(ingredients);
      const batterInPan = pour(batter, pan);
      const bakedCake = bake(batterInPan, 350, 45);
      const cooledCake = coolOff(bakedInPan);
      return separateFromPan(cooledCake);
    }
Even if you consider the `const` to be visual noise, the names are useful. At any point you can understand the goal of the code on the right-hand side by looking at the name of the variable on the left-hand side. You can also visually scan the right-hand side and see the processing steps. You can also introduce new steps to the control flow at any point and understand what the data should look like both before and after your new step.

I agree that the the control flow is more clearly elucidated in the pipe operator example, but it tosses away useful information about the state that the named variables contain. It also introduces two new syntactical concepts for your brain to interpret (the pipe operator and the value placeholder). I contend the cognitive load is no greater in the example with names, and the maintainability is greatly improved.

If you have an example where there are dozens of steps to the control flow with no break, I'd be really curious to see it.

Re: Pipe Operator (|>) For JavaScript

#175

Earlier quoted context omitted.

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.

There's nothing preventing you from not using the pipe for last call or even introducing that rule in your team if you can convince your colleagues it's a good idea or even automate it with the use of a linter.

If it's really good rule you can advocate for it at this stage. It might be prudent to use |> % only inside function call parameter or possibly as right hand of an assignment instead of everywhere where parser expects an expression.

a(b(),c(), g() |> f(%) |> d(e(), %))

Although I'll be honest, I don't like it. Other parameters of a() call for me occlude the flow and the intent.

We could make it a bit better with newlines.

    a(b(),c(), 
        g() |> f(%) |> d(e(), %))
    )
But if a() takes more parameters after the main one then we have the same problem as usual where parts of the same call can end up far away from one another.

    a(b(),c(), 
        g() |> f(%) |> d(e(), %))
    x(), y(), z())

For me

    g() |> f(%) |> d(e(), %))
    |> a(b(), c(), %, x(), y(), z());
is still better and I wouldn't want it to be prevented by language syntax.

Re: Pipe Operator (|>) For JavaScript

#176

This makes me nervous. In general, I think adding features like this to a mature language is a misstep because it increases the cognitive load of "things you have to know to read other people's code." And that's strictly increases... Since changes like this can't remove previous approaches (for backwards compatibility reasons), we'll now have three syntaxes for function calls? Yuck. Left unchecked, this predilection…

I'm starting to think the entire direction of modern Javascript is one giant yak-shave built on enabling bad decisions.

"I like functional programming so I'm going to do that in JavaScript" -> "Now I have a problem because JavaScript is not very good at that, so now let's radically alter JavaScript until it's... well, still not good at it, but it looks like it is, at a glance" (initially through libraries, now altering the language itself)

"Let's make as many calls async by default as possible" -> "Oh but actually I need most of them to seem synchronous, even if they're actually async, like 90+% of the time" -> "Callbacks?" -> "Oh god that sucked... promises?" -> "Better but still not great, let's just... uh... add `async` and `await` and watch as their use becomes so hilariously common that it's now painfully obvious that the default behavior is wrong?"

Re: Pipe Operator (|>) For JavaScript

#177
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…

The issue with taking examples from real-world code & converting them is that there's no guarantee the real-world code is good. It usually isn't, so you're comparing bad with bad.

A more aggressive reformulation would be to prefix the original code with

  const envOutput = Object.keys(envars).map(envar => `${envar}=${envars[envar]}`).join(' ');
  const argsOutput = args.join(' ');
Leaving the example being converted as simply:

  console.log(chalk.dim(`$ ${envOutput}`, 'node', argsOutput));
vs

  envOutput |> `$ ${%}` |> chalk.dim(%, 'node', argsOutput) |> console.log(%);
This more clearly highlights the obvious limitations of pipelines - piping envOutput but not 'node' nor argsOutput is a jarring syntax-mix here. Though I think it offers some hope for them working well in other scenarios - possibly in curry-heavy applications.

Re: Pipe Operator (|>) For JavaScript

#178
post #84

This strikes me as something better left to libraries. If you want to write in a functional style then Ramda, Lodash, Underscore, and plenty of others have pipe and compose functions. pipe(one, two, three) Easy to read. No new syntax. Extendable with arrow functions. Yes, there are some limitations in comparison to Hack Pipes. But those are far outweighed by not messing yet again with the language’s syntax.

This only applies to TypeScript, but error checking for this pattern from a library is much harder than error checking for native syntax.

In order for TypeScript to check if the return value of one function has the correct type for the next function, you need to use generics, but generics don't allow for a variable number of generic parameters, so `pipe` would just have to use unknown or have dozens of overloads for each length of pipe usage (within reason).

I know TypeScript is a different, optional language, and I see no reason why they couldn't add the feature without it being in JavaScript, but that's not generally how TypeScript operates. If JavaScript doesn't add it, TypeScript won't. There's also lots of tooling that will type check your JavaScript when available, which will benefit from a simpler model.

Re: Pipe Operator (|>) For JavaScript

#179
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.

My JS code looks exactly like you describe. Just a bunch of const (rarely let) statements with descriptive, short names. It's not tedious at all, just a little verbose. But JS is already a language that is relatively compact so it doesn't really matter.

Re: Pipe Operator (|>) For JavaScript

#180
post #161

Earlier quoted context omitted.

Nested calls can be a code smell, sure. But easily fixable: one(two(three())); Becomes let a=three(); let b=two(a); one(b); Clean and easy, no sugar required.

Lots of low-value names required. Those names will inevitably either be badly chosen or have taken far more time to make up than they are worth. That's assuming the code isn't written by that one guy on every team who stubbornly insists on var x = three(); x = two(x); one(x): (having the names certainly is nice to have in the debugger, but I'd rather have those intermediate results be an explicit debugger feature tha…

Good point and yours does look better :)
Post reply on HN