DEV Community

Discussion on: Future Javascript: Javascript Pipeline Operators

Collapse
akashkava profile image
Akash Kava

Can anyone explain benefit of this? Except for different syntax, there is no performance benefit. Almost same number of characters are required to write same logic it isn’t even smaller syntax except you can write each one in new line.

This way there are million different syntax offered by various language, bringing all in JavaScript will be complex.

Collapse
peerreynders profile image
peerreynders • Edited on

Can anyone explain benefit of this?

In OO programming there is the concept of the fluent interface that allows you to chain method calls without needing intermediate assignments — this for example features prominently in the fluent builder patterm.

The disadvantage of this approach is that:

  • the method has to return an object (which in most cases is just simply this) and
  • the method the next "step" needs to perform has to be present on the returned object. So if you're dealing with with a third party object and you want to use your helper function then you are out of luck (unless you are OK which monkey patching the object first)

In value-oriented programming (most people call it functional programming though that definition is actually narrower) computation is advanced through the "transformation of values" rather than "flow of control".

So with pipelining you can use the "fluent chaining style" on any value including Primitve Values and objects without the required method/function.

Another way of writing

const calculate = addOne(multiplyByTwo(divideBySix(number)));
Enter fullscreen mode Exit fullscreen mode

is

const fns = [divideBySix, multiplyByTwo, addOne];
const calculate = fns.reduce((x, f) => f(x), number);
Enter fullscreen mode Exit fullscreen mode

The advantage with the second approach is that the function applications are now ordered left-to-right which many people find easier to read. The downside is the overhead of reducing an array of functions at runtime when the order and composition can fixed at design time ("Bake, don't fry"). Using the pipeline fixes the the reading order of the functions while not incurring any additional runtime overhead. Ideally the placeholder should be optional so that

const calculate = number |> divideBySix(%) |> multiplyByTwo(%) |> addOne(%);
Enter fullscreen mode Exit fullscreen mode

could be replaced with

const calculate = number |> divideBySix |> multiplyByTwo |> addOne;
Enter fullscreen mode Exit fullscreen mode

for single argument functions but I don't think that is even considered in the current proposal (ReScript has the advantage of resolving the placeholder at compile time; perhaps there are JS runtime performance concerns with an optional placeholder);

This way there are million different syntax offered by various language, bringing all in JavaScript will be complex.

Perhaps you are forgetting that JavaScript started out as "doing Scheme in the browser" — making it vaguely look like Java was a marketing stunt.

The later has better visibility and easy to debug.

Frankly that might simply be a result of your own familiarity bias. Most people who were taught imperative programming first, initially perceive functional programming as "less natural" and more difficult. But educator's experience has shown that is largely baby duck syndrome; in fact many students find it easier to start with functional programming first though there are always exceptions.

It should be no more difficult to follow the piped functions in the debugger or throw in a tee function.

const number = 6;
const fns = [divideBySix, multiplyByTwo, tee, addOne];
const calculate = fns.reduce((x, f) => f(x), number);

console.log(calculate);

function tee(x) {
  console.log(`tee: ${x}`);
  return x;
}

function addOne(x) {
  return x + 1;
}

function multiplyByTwo(x) {
  return x * 2;
}

function divideBySix(x) {
  return x / 6;
}
Enter fullscreen mode Exit fullscreen mode

Being imperatively minded also means thinking predominantly in "statements" as computation is about "place-oriented" data manipulation and "flow of control". Value-oriented programming relies heavily on "expressions" instead as computation is expressed as "transformations of values" and the pipe operator expands the expression toolkit.

The reason I am asking as I have written JavaScript engine for .NET,

Was F# ever considered as an option especially given that ML languages are considered to be good for writing compilers?

I understand that

  • hypothetically it should be easier to find C# contributors as F# programmers aren't as common
  • C# may currently have some performance advantages (e.g. asynchronous tasks)
  • C# seems like the safer choice

The issue is that there is already a precedent with Rhino. While it's still around, it seems to be languishing despite being based on something as "safe" and prevalent as Java. And Oracle has abandoned Nashorn in favour of supporting JS directly on GraalVM — removing Java as an intermediary.

Another approach would be to simply create a runtime that implements some smaller instruction set (like ES3) as generated by the Closure Compiler.

That said the pipeline operator proposal is only at Stage 2, with reservations as of August 2021 — so it may never happen and even in the best case wouldn't enter the spec until 2023 at the earliest.

I would imagine that Pattern Matching would cause a lot more work than the pipeline operator.

Collapse
akashkava profile image
Akash Kava

Thanks for detailed answer, I do find sequence of functions in correct order as important thing as nested function calls are evaluated from depth first order.

Collapse
bamartindev profile image
Brett Martin

Basically its just for readability. In the example above:

addOne(multiplyByTwo(divideBySix(number)))
Enter fullscreen mode Exit fullscreen mode

It takes a bit to parse what is happening, and you have to work inside out. Using the pipeline operator you can make it read a bit more step by step, and you can chain a lot of functions together without all of the parens.

Collapse
akashkava profile image
Akash Kava

Parsing benefit is very small compared to feature overhead. Also for better debugging chaining functions should be avoided, instead minizers can chain functions to reduce code size. And what about multiple parameters? I see no difference in terms of parsing benefit and syntax in below both styles.

var result = a 
    |> b(%) 
    |> c;
Enter fullscreen mode Exit fullscreen mode
var x = b(a);
      x = c(x);
var result = x;
Enter fullscreen mode Exit fullscreen mode

The later has better visibility and easy to debug. The reason I am asking as I have written JavaScript engine for .NET, and we are incorporating more features and I want to implement upcoming features as part of test drive.

I certainly want to know the real benefit, for example ?? has benefit over || as ?? will only check for null or undefined and || will first call valueOf for objects and check if string is empty or not, leading to slower execution. ?. is not only a shorter syntax but faster execution as it will generate smaller machine instructions to duplicate value on stack without causing any overhead of storing value in a variable and evaluating it.

GitHub logo yantrajs / yantra

JavaScript Engine for .NET Standard

YantraJS

Yantra (Machine in Sanskrit) is a Managed JavaScript Engine for .NET Standard written completely in C#.

NuGet

Name Package
YantraJS (With CSX Module Support) NuGet
YantraJS.Core (Compiler) NuGet
YantraJS.ExpressionCompiler (IL Compiler) NuGet
WebAtoms.YantraJS NuGet

Documentation

  1. Introduction
  2. Expression Compiler
  3. JavaScript Engine

Discussions

We recommend using Github Discussion on this repository for any question regarding this product.

Special Thanks

  1. We are thankful to authors of Jurassic (we have incorporated number parser, promise and some unit tests from Jurassic.) github.com/paulbartrum/jurassic
  2. We are thankful to authors of EsprimaDotNet, we initially built prototype over EsprimaDotNet, but we chose to build our parser/scanner from scratch to support token spans. github.com/sebastienros/esprima-do...
  3. We are thankful to author of ILPack (we have incorporated saving IL to Assembly from this library.) github.com/Lokad/ILPack
Thread Thread
bamartindev profile image
Brett Martin

For the first two points, if you are writing JS in a functional style then chaining functions is used often to compose functionality. In those instances, having a single input function is more common.

As far as the second example, if you want to avoid mutations / shadowing, the reassigning variables is something to avoid.

Again, these are personal choices on how to write the JS code. I guess its just part of the challenges of having multiple paradigms in a language. Here is the full proposal though, it will hopefully give more insight into the rationale: github.com/tc39/proposal-pipeline-...

Its still in Stage 2 and there appears to be outstanding questions around it since August.