2

Does the Hack-style pipe operator |> take precedence over grouping operator ( ) in order of operations in JavaScript?

I'm investigating tc39/proposal-pipeline-operator - for JavaScript


Pipe Operator (|>) for JavaScript

There were two competing proposals for the pipe operator: Hack pipes and F# pipes.


The minimal/F#-style pipe-operator is just a binary operator of function application between a value x and a function f in algebraic sense that is:

f(x) === x |> f

g(f(x) === x |> f |> g

As this is a simple replacement of the mathematical expressions, there is nothing to relearn and so-called referential transparency is guaranteed.

Referential transparency and referential opacity are properties of parts of computer programs. An expression is called referentially transparent if it can be replaced with its corresponding value (and vice-versa) without changing the program's behavior.


Now, they have chosen the Hack-style pipe advanced to TC39 Stage2.


Pro: The righthand side can be any expression, and the placeholder can go anywhere any normal variable identifier could go, so we can pipe to any code we want without any special rules:

  • value |> foo(^) for unary function calls,
  • value |> foo(1, ^) for n-ary function calls,
  • value |> ^.foo() for method calls,
  • value |> ^ + 1 for arithmetic,
  • etc.

Although the Hack-pipe proposal team claims as

Pro: The righthand side can be any expression

this means the type of |> is no longer as simple as the type of minimal/F#-style pipe-operator:

  • x : Object
  • f : Function

Therefore, I need to investigate what's really going on underneath using Babel: Implemented in v7.15.


Test-1

REPL with an example code with a configuration

Image: OPTIONS - Pipeline proposal - Hack

const f = a => a * 2;
const g = a => a + 1;
 
1 |> f(%) |> g(%);
1 |> (f(%) |> g(%));

transpiled to

Image of Babel transpile

var _ref, _ref2, _ref3, _ref4;
const f = a => a * 2;
const g = a => a + 1;

_ref2 = 1, (_ref = f(_ref2), g(_ref));
_ref4 = 1, (_ref3 = f(_ref4), g(_ref3));

which indicates

  • 1 |> f(%) |> g(%)
  • 1 |> (f(%) |> g(%))

the both expressions share an identical structure under the Hack-pipe.

(I have confirmed this result is on-spec and expected from one of the champion team of Hack-pipe proposal)

In the principle of Grouping operator ( ) in JavaScript, this should be invalid.

The grouping operator ( ) controls the precedence of evaluation in expressions.

The grouping () rules the mathematical structure (dependency graph) of expressions.

In mathematics, computer science and digital electronics, a dependency graph is a directed graph representing dependencies of several objects towards each other. It is possible to derive an evaluation order or the absence of an evaluation order that respects the given dependencies from the dependency graph.

Surely, there is a factor of evaluation order by the evaluation strategy (eager evaluation for JavaScript), however, the algebraic structure (dependency graph) should be changed accordingly,

      |>
     / \ 
    |>  g(%)
   / \ 
  1  f(%)

     |>
    / \ 
   1   |>  
      / \ 
    f(%) g(%)

and the transpile fact shows the Hack-pipe ignores the principle.

See the great answer for Does the functionality of Grouping operator () in JavaScript differ from Haskell or other programming languages?


Test-2

So, If the Hack-pipe follows the rule of the Grouping operator in JavaScript, or any other programming languages, for the expression:

1 |> (f(%) |> g(%));

regardless of the evaluation order, the dependency graph should be:

     |>
    / \ 
   1   |>  
      / \ 
    f(%) g(%)

Now I have a log function to show the value.

const right = a => b => b;
const log = a => right(console.log(a))(a);

This behaves like identity function: a => a which does not affect to the original code but console.log(a) in the process.

Now, we want to know the evaluated value of (f(%) |> g(%))

1 |> (log(f(%) |> g(%)));

which transpiled to

Image of Babel transpile

and the console.log result is 3 regardless of the order of the evaluation.

(f(%) |> g(%)) == 3

where

const f = a => a * 2;
const g = a => a + 1;
1 |> f(%) |> g(%);    // 1 * 2 + 1 = 3
1 |> (f(%) |> g(%));  // 1 * 2 + 1 = 3 with hack-pipe

therefore,

1 |> 3 == 3

which indicates Hack-pipe is logically broken and does not make sense any more to code.


My question, or what I would like you to distinguish is:

Does the Hack-style pipe operator |> take precedence over grouping operator ( ) in order of operations in JavaScript?

Objective answers not subjective/opinion base, please. Thanks.



For comments:


1.

We share the fact that the Grouping operator has the highest precedence according to Operator precedence.

enter image description here


2.

According to the tc39/proposal-pipeline-operator Hack proposal

enter image description here

Yes, this is a claim by a member of the champion team of the Hack pipe proposal.

Now, the claim is against the fact that I examined here, and that is why I want you distinguish, including what I miss in my investigation here.


The purpose of the question is to know the fact of the hack-pipe eliminating obscurity in interpretation or ignoring the exact implementation underneath.

KenSmooth
  • 275
  • 1
  • 10
  • 1
    "Override" is an unfortunate choice of words, as it is most commonly used to describe two methods that have the same name but different parameter lists. Perhaps your actual question is: *"Does the |> operator take precedence over parentheses in order of operations?"* – Robert Harvey Oct 01 '21 at 22:42
  • The rest of your question appears to be window dressing, unless you're asking for something else. I admire your attention to detail, but at the end of the day the behavior of these operators is whatever it is. – Robert Harvey Oct 01 '21 at 22:44
  • @Robert Harvey True, any suggestion for the proper wording? – KenSmooth Oct 01 '21 at 22:44
  • Didn't I give a suggestion for the proper wording? – Robert Harvey Oct 01 '21 at 22:44
  • Also, this would be easy enough to test with actual code, would it not? It would probably take five minutes to find out, and then you'd know. – Robert Harvey Oct 01 '21 at 22:45
  • @Robert Harvey I really appreciate your advice. I've changed the title, and yes, I've tested with the actual code as illustrated, and since I believe this issue is substantial, I am willing to be examined by third persons to make sure. – KenSmooth Oct 01 '21 at 23:41
  • Fair enough, but none of us have the power to change the precedence of that operator. Your *only* chance is to take the matter up with Microsoft. – Robert Harvey Oct 02 '21 at 01:41
  • @Robert Harvey The purpose of the question is to confirm the fact. I've been very careful to examine the whole aspect, but as I said, I'm willing to be examined. In fact, I also would like to ask you to answer the topic. (I will upvote for sure) "Does the Hack-style pipe operator |> take precedence over grouping operator ( ) in order of operations in JavaScript?" If yes, your reasoning, if no, your reasoning because I don't think I covered up everything here. Thanks in advance. – KenSmooth Oct 02 '21 at 02:14
  • 1
    The question "Does the Hack-style pipe operator |> take precedence over grouping operator ( ) in order of operations in JavaScript?" can most likely be answered with *three lines of code.* All you have to do is write them. If that isn't sufficient... – Robert Harvey Oct 02 '21 at 02:29
  • 1
    ... you can read [this post](https://github.com/tc39/proposal-pipeline-operator) which states that: *"The pipe operator’s precedence is the same as: the function arrow =>; the assignment operators =, +=, etc.; the generator operators yield and yield *;"* – Robert Harvey Oct 02 '21 at 02:34
  • [This post](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence) states that parentheses takes precedence over everything else, as I already suspected. – Robert Harvey Oct 02 '21 at 02:36
  • @Robert Harvey Thank you for pointing it out, and actually I should have included the both information here. I had read that, and I've add them at the end of my question. Thanks. – KenSmooth Oct 02 '21 at 04:38
  • 1
    "can most likely be answered with three lines of code. All you have to do is write them." Please do in your answer, I really appreciate that. – KenSmooth Oct 02 '21 at 04:38
  • 1
    There is a LOT of material here for a relatively simple core question, much of it only somewhat relevant. I applaud the effort you've gone in to investigate and answer this yourself, but it's not very easy reading. To be honest, I probably wouldn't have bothered to read this thoroughly enough to answer it if you hadn't specifically asked me to (though I wouldn't have seen it anyway, as I don't usually haunt these tags). – Ben Oct 02 '21 at 12:03
  • @Ben First of all, I really appreciate your answer in any way. Then, I'm sorry for the amount of document but since this hack-pipe should be a totally new idea for the majority of readers, I thought I need to clarify the background knowledge so that the readers can avoid their own research on this. – KenSmooth Oct 02 '21 at 12:14

5 Answers5

3

I don't think "the pipe operator is taking precedence over grouping parentheses" is the right way to look at this.

The OP is essentially asking how these can do the same thing:

  1. 1 |> f(%) |> g(%)
  2. 1 |> (f(%) |> g(%))

The OP sees that f(%) |> g(%) on its own cannot be a valid expression, but it seems that #2 requires evaluating this as a sub-expression, while #1 doesn't seem to require this. The OP's explanation is that in #2 the |> operator is taking precedence over the grouping parentheses, and is actually equivalent to #1 even though it does not look like it can be.

I don't like that explanation of how #2 works. Any operator "taking precedence over parentheses" sounds like confusing nonsense to me1. So I want a way of conceptualising what is happening in #2 that (a) gives me the correct intuition for what the result is and (b) respects the fundamental rules of how parentheses work. The following is how I would conceptualise it after reading the proposal, and some brief experiments with the Babel implementation the OP linked.

Firstly, note that |> is not an operator in the same way that + or - or ! is an operator. All of those stand for computations in the same way that user-defined functions do; they take values as arguments. 1 + 2 is the same as x + y in a context where we have x = 1; y = 2. + operates on the value that results from evaluating its operands, and doesn't care what particular expression we wrote down in the source code for those operands.

|> isn't like this. Its arguments are expressions, not values, and the right argument isn't even a normal expression, but one that must contain a topic reference %. |> cares about code you write for its operands, not just the result of evaluating its operands. 1 |> f(%) is valid, but you can't use z = f(%); 1 |> z. %, and template expressions containing %, are not first-class values. The rules of evaluating expressions built from |> and % cannot be built by just adding new operators that take values as arguments. They need to be specifically "hard-wired" into the language, in a way that ordinary operators do not.

Notice how I've been talking about different explanations rather than whether they are correct or incorrect. To a certain extent this is a question of how you prefer to think of it, not absolute truth. |> cannot be an ordinary operator at all; it has to be part of the syntax of the language. We often think about language syntax in ways that have little to do with any actual implementation strategy, so I'm not going to say it's outright invalid to think of this case is that |> can take precedence over parentheses. If you can build a consistent set of rules around that idea, feel free to conceptualise it that way, but I will not be using that option.

However, since the language has made the choice to make |> look much like an ordinary operator, I choose to believe we're supposed to interpret expressions containing it in a fairly similar way, not totally overriding other rules of syntax like parentheses. In particular, I prefer to insist that 1 |> (f(%) |> g(%)) is 1 piped to f(%) |> g(%), not 1 piped to f(%) and then the result of that piped to g(%).

My position would fall apart if I couldn't give a consistent interpretation to what f(%) |> g(%) means. However it has an obvious meaning; in x |> 1 + % we get the result by taking the right hand side and substituting the left hand side where the % is, giving 1 + x. I say: just do the same thing with f(%) |> g(%). The result is g(f(%)), which still contains a topic reference and so is still invalid except as the right hand side of an outer |> operator.

So 1 |> (f(%) |> g(%)) ends up meaning g(f(%)), which is the same thing that (1 |> f(%)) |> g(%) means, even though it gets there by a slightly different route.

This also helps explain what is going on in the OP's example 1 |> (log(f(%) |> g(%))). Expressions containing % are not first class values and cannot be passed to log to be printed. My explanation for f(%) |> g(%) says that log(f(%) |> g(%)) means log( g(f(%) ). This is still a template expression containing %, and cannot be evaluated on its own; it must occur to the right of a |> operator. So 1 |> (log(f(%) |> g(%))) ends up meaning log( g(f(1)) ). This makes it unsurprising that the OP gets 3 printed in the console; that is not proving that f(%) |> g(%) is somehow equivalent to 3, and the fact that 1 |> 3 is a syntax error does not tell you anything about 1 |> (f(%) |> g(%)).

This is also where I suspect you'll have trouble building a consistent interpretation of the pipe syntax using the "takes precedence over parentheses" idea; you have to also start saying things like "the pipeline takes precedence over function applications, so that 1 |> log(f(%) |> g(%)) actually is interpreted as log(1 |> f(%) |> g(%)). Maybe you can carry that all the way through and have a model for pipelines that actually gives the correct results in all cases, but it seems much more complicated and confusing than is necessary than just accepting that one of the weird things about |> is that f(%) |> g(%) is an expression that itself results in a template, not a value.


1 Although there's nothing stopping a language designer putting confusing nonsense into the rules of their language, and JavaScript is arguably not without its share already.

Ben
  • 68,572
  • 20
  • 126
  • 174
  • I really appreciate your elaboration every time, and now I wrote my response to your answer at the bottom of my question. Thanks. – KenSmooth Oct 03 '21 at 01:00
  • 1
    @KenSmooth I think I missed the most important point in your question, which was the `1 |> log(f(%) |> g(%))` example. I started trying to edit my answer a bit to focus more on that, and ended up completely rewriting it. I hope this is more useful now! – Ben Oct 03 '21 at 09:42
  • 1
    @KenSmooth One thing worth noting; this is not supposed to be a discussion site. It's not generally a good idea to make substantial edits to your question in order to "reply" to an answer. If you would like to edit the question to make the question-and-answer more useful to future visitors with similar thoughts (and get more upvotes), I would recommend paring it right back to just a brief explanation of what `|>` and `%` do (keep the links to the proposal and babel), and the core examples of `1 |> (f(%) |> g(%))` and `1 |> log(f(%) |> g(%))`. It shouldn't need more than a page, IMHO. – Ben Oct 03 '21 at 09:48
  • Thanks for your rewriting and now I feel your analysis to my investigation has become much more reasonable to me than the fist edition. I sincerely will upvote this edition, and post my response as my own answer here. – KenSmooth Oct 03 '21 at 19:53
  • I made my post as answers to you and @Bergi. https://stackoverflow.com/a/69429765/10638454 https://stackoverflow.com/a/69431334/10638454 My response to you includes EDIT section. – KenSmooth Oct 04 '21 at 05:48
2

In response to your response :-) You posted a new line of thought that warrants a separate answer, which might however also clear up your original confusion.

It's reasonable for us to consider (f(%) |> g(%)) must have some value regardless of the evaluation order by JavaScript runtime.

The expression on the right of |> is in some ways a template for a real expression, with the % marking a "hole" that needs to be filled by something. So this is new - this is a whole new concept in the history of JavaScript.

No, this is not new at all. "A template expression with a hole that is filled by something" is basically just a function. The "hole" (called topic in the proposal) is just a constant with a local definition in the right-hand side of the |> operator. Taking basic lamda calculus syntax for introducing constants, we can say that an expression

lhs |> rhs

is syntactic sugar for

(% => rhs)(lhs)

where % is assumed to be an identifier, the parameter of the anonymous arrow function.

With this in mind, we can look at what grouping operator does to the expression 1 |> f(%) |> g(%);. Let's first realise that |> is right-associative, so the parse tree of the expression is

  |>
 / \ 
1   |>  
   / \ 
 f(%) g(%)

and is evaluated as

(% => (%x => g(%) )(f(%)) )(1);

With the grouping 1 |> (f(%) |> g(%));, nothing changes:

(% => ((%x => g(%) )(f(%))) )(1);
//    ^                   ^

but with (1 |> f(%)) |> g(%);, we get

(% => g(%) )( ((%x => f(%) )(1)) );
//            ^                ^

1 |> (f(%) |> g(%)) contains the sub-expression (f(%) |> g(%)), so clearly that must be meaningful as an expression in its own right.

The only additional restriction that the hack pipeline proposal imposes is that you can use % topic references only on the right-hand side of an |> operator. It is not valid in an arbitrary expression on its own. So while I can assign some denotational semantics to % and |> about their evaluation using lambda calculus, it is not possible for javascript programs to observe this intermediate result value, we can never refer to that arrow function I described above. This is necessary to give the compiler the freedom to optimise this properly.

In particular, when you write 1 |> (log(f(%) |> g(%))); you cannot substitute the subexpression (log(f(%) |> g(%))) with 3. The flaw in this idea is that you think by adding the log call, you would get an evaluation as

  (log(% => (%x => g(%) )(f(%)) ))(1);
// ^^^^                         ^

but actually it does become

  (% => log((%x => g(%) )(f(%))) )(1);
//      ^^^^                   ^
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • I made my post as answers to @Ben https://stackoverflow.com/a/69429765/10638454 and Bergi. https://stackoverflow.com/a/69431334/10638454 – KenSmooth Oct 04 '21 at 14:24
2

This is my own Answer after reading Answers by @Ben and @Bergi. My comments to respond to them under their Answers will not be sufficient in terms of the complexity and readability to readers, and editing the Question will also have the same problem for the amount of the document. (the previous duplicated section has been removed from the Question and moved here)

My response to Answer by @Ben


Common ground between us: (Please feel free to express your objection)


  • Grouping parentheses mean the same thing in Haskell as they do in high school mathematics. They group a sub-expression into a single term. This is also what they mean in JavaScript.

  • think of evaluation order as a totally separate concept than grouping.

quoted from your answer: https://stackoverflow.com/a/69386130/11316608

  • 1 |> (f(%) |> g(%)) contains the sub-expression (f(%) |> g(%)), so clearly that must be meaningful as an expression in its own right.

if the grouping () shares the same functionality in mathematics, it's reasonable for us to consider (f(%) |> g(%)) must have some value regardless of the evaluation order by JavaScript runtime.

     |>
    / \ 
   1   |>  
      / \ 
    f(%) g(%)
  • The expression on the right is in some ways a template for a real expression, with the % marking a "hole" that needs to be filled by something. So this is new;

This is a whole new concept in the history of JavaScript.

  • Also note that it doesn't matter how this actually works at a lower level in any given implementation, so staring at the output of the babel transpiler is not necessarily the way to reach enlightenment.

Correct, the only reason I've used that is to prove the actual behavior, and I am also the one who is not that interested in how it works underneath.


Consequently, regardless of your analysis what's going on underneath with the Hack |> and the placeholder %, I wrote:

1 |> (log(f(%) |> g(%)));

You can check the full code in this Question.

and the result is 3, so,

1 |> (f(%) |> g(%)) ===
1 |> 3;

Apparently this does not make sense anymore, and this is a syntax-error

enter image description here

therefore, it's reasonable to observe, in order to make sense, the only way is to refuse the operation/evaluation of (f(%) |> g(%)) that I would call "the Hack-style pipe operator |> takes precedence over grouping operator ( ) in order of operations in JavaScript".

Also note that we the potential user of this Hack pipe really don't care what's going on underneath, and the Bebel transipiler implemented by the champion team just helps for the fact check.


My response to the second edition of the answer:

Common ground between us: (Please feel free to express your objection)


  • |> cannot be an ordinary operator at all; |> is not an operator in the same way that + or - or ! is an operator which stand for computations in the same way that user-defined functions do; they take expressions/values as arguments.

  • The right argument of |> isn't even a normal expression, but one that must contain a topic reference %.

  • |> looks much like an ordinary operator, so it's reasonable for us to believe to interpret expressions containing it in a fairly similar way, not totally overriding other rules of syntax like parentheses.

  • It's Not outright invalid to think of this case is that |> can take precedence over parentheses.


I somehow understand your intuition against my question, however, I also understand you no longer deny the perspective.

This is also where I suspect you'll have trouble building a consistent interpretation of the pipe syntax using the "takes precedence over parentheses" idea;

I'm afraid to say the statement is not valid.

I will not have any trouble building a consistent interpretation of the pipe syntax if I'm Not using "takes precedence over parentheses" idea.

The fact is that we will have trouble building a consistent interpretation of the pipe syntax under the estimation of "the hack pipe never takes precedence over parentheses" idea, and again, I'm afraid to say, in your answer, I feel somewhat such an aspect has been justified by repeating to show the process underneath. In fact, I already understand how it behaves underneath and share the context of your explanation. I am not confused about what you say.


EDIT: I added new information.

My position would fall apart if I couldn't give a consistent interpretation to what f(%) |> g(%) means.

My explanation for f(%) |> g(%) says that log(f(%) |> g(%)) means log( g(f(%) ).

So, before the placeholder % is sufficed by a concrete value, it is like:

f(%) |> g(%) means % => g(f(%)) that is the function composition of f and g.

In algebraic sense (the mininal/F#-style), the pipeline-operator has been explained as

f(x)    === x |> f 
g(f(x)) === x |> f |> g

and if . should be the operator for function composition,

g(f(x)) === x |> (f . g)

|> itself is not associative in Monoid.

For Hack pipe, on the other hand,

g(f(x)) === x |> f(%) |> g(%)
g(f(x)) === x |> (f(%) |> g(%))

|> seems to be associative in Monoid but cannot be in a mathematical sense. With hack |>, the binary operator for function application switch to the role of function composition once the sub-expression is closed with ().

g(f(x)) === x |> (f . g)(%)

you have to also start saying things like "the pipeline takes precedence over function applications, so that 1 |> log(f(%) |> g(%)) actually is interpreted as log(1 |> f(%) |> g(%)) ".

Correct, and when I observe (observed in really already)

1 |> log(f(%) |> g(%)) === 
log(1 |> f(%) |> g(%))

and we are told to accept the reality of the spec of the hack-pipe, I firmly believe no one here can control the code with hack pipe any more.

How about this??

log( 1 |> log(f(%) |> g(%)) )

log( log(1 |> f(%) |> g(%)) )

or

log( 1 |> log(f(%) |> log(g(%))) )

log( log( log(1 |> f(%) |> g(%)) )

Since apparently, the grouping operator ( ) seems no longer to control the dependency graph of expressions under the Hack-pipe influences, regardless what's going on underneath, if you stands on the same ground

  • Grouping parentheses mean the same thing in Haskell as they do in high school mathematics. They group a sub-expression into a single term. This is also what they mean in JavaScript.

  • think of evaluation order as a totally separate concept than grouping.

I must maintain my own hypothesis Hack-style pipe operator |> takes precedence over grouping operator ( ) in order of operations in JavaScript

Although there's nothing stopping a language designer putting confusing nonsense into the rules of their language, and JavaScript is arguably not without its share already.

I agree with you in terms of this is merely putting confusing nonsense into JavaScript, please note this hack pipe proposal has just advanced to Stage-2.

JavaScript is arguably not without its share already.

Perhaps, but generally speaking, such a fact does not justify the extra confusion nonsense; especially for the substantial binary operator of function application f(x); for minimal/F# proposal is f(x) === x |> f, and the Hack proposal is not as we confirmed.


I will separate my answer responding to @Bergi.

KenSmooth
  • 275
  • 1
  • 10
  • "*`|>` is not an operator in the same way that `+` or `-` or `!` is an operator which stand for computations in the same way that user-defined functions do; they take expressions/values as arguments. The right argument of `|>` isn't even a normal expression, but one that must contain a topic reference `%`.*" - this is true, but consider that few operators in JS are "ordinary". Think of `delete`, `new`, property access, the conditional `?:`, etc. All of these operate on expressions. – Bergi Oct 04 '21 at 00:57
  • @Bergi That are the statements, and now we discuss operators. I'm afraid to say that you have enhanced the range of the wording that we have discussed between Ben and I. Same goes to the placeholder of functions, I should have limited the first placeholder talk to operators in JS. – KenSmooth Oct 04 '21 at 01:03
  • @Bergi I double-checked in MDN and was surprised to see they explain it as expressions, and this is another long story, but to me `delete` `new` is the same league of the statements such as `const` or `let` that is not the expressions to operate operands without states but some order to change the states of some variable itself. – KenSmooth Oct 04 '21 at 01:19
1

No, it does not take precedence over the grouping operator. However, the |> operator is right-associative, which means that

1 |> f(%) |> g(%)

and

1 |> (f(%) |> g(%))

form parse trees of the same shape:

  |>          |>
 / \         / \
1   |>      1  ( )
   / \          |
f(%) g(%)       |>
               / \
            f(%) g(%)

To get the dependency tree nested the other way round, you can use the grouping operator on the the first clause:

(1 |> f(%)) |> g(%)

becomes

    |>
   / \ 
 ( )  g(%)
  |
  |>
 / \ 
1  f(%)

Proof in Babel REPL:

transpiler output of the three expressions nested differently

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • 1
    Thanks for your insight. Bergi. Honestly, I really like your answer a lot with the graph, however, I'm afraid to say that *IF* the associativity of the binary operator `|>` has such an ability to transform the dependency graph composed by the grouping operator `()`, I think that is the evidence of *"the Hack-style pipe operator `|>` take precedence over grouping operator `( )` in order of operations in JavaScript"*. Wouldn't you agree?? – KenSmooth Oct 02 '21 at 09:02
  • For the definition/mathematical meaning of "Grouping" `()` is already clarified by the QA "Does the functionality of Grouping operator `()` in JavaScript differ from Haskell or other programming languages?" https://stackoverflow.com/a/69386130/11316608 , I think and if you did not agree to the concept, I appreciate your counter perspective as new answer there but if you agree to the QA contents, you simply have proven that "it does take precedence over the grouping operator" with your answer here.. – KenSmooth Oct 02 '21 at 09:48
  • For future references, Associative property in Algebra means simply the equality of the value of the symmetric expressions (dependency graph), and there is not such a concept in Associativity to transform the expression (dependency graph) in Algebra. At least, the value `(f(%) |> g(%))` should exist, **Hack pipe operator is not even associative** because in my example, the value is `3`. Or if they claim the evaluation is impossible, it's also we cannot accept, the hack operator has a property of associative. – KenSmooth Oct 02 '21 at 09:50
  • 3
    @KenSmooth "*IF the associativity of the binary operator `|>` has such an ability to transform the dependency graph composed by the grouping operator `()`*" - no, you misunderstand how [operator associativity](https://en.wikipedia.org/wiki/Operator_associativity) works. It is not related to the to mathematical associative property of an operation. The right-associativity does not "transform" the dependency graph of the expression that includes the grouping operator - that is already determined by the precedence. The right-associativity comes into play in the expression that is *not* grouped. – Bergi Oct 02 '21 at 12:12
  • 1
    @Bergi Sure, I thought we had talked about [Associative property](https://en.wikipedia.org/wiki/Associative_property) as the mathematical concept since I thought we could agree on the functionality of grouping operator `()` is mathematics. Having said that it seems @Ben is also against so after reading the whole, please let me respond to your insight again. Thanks. – KenSmooth Oct 02 '21 at 12:36
  • 1
    @KenSmooth "*the functionality of grouping operator `()` is mathematics.*" - not sure what you mean. It's required for explicit and unambiguous nesting of expressions for parsing them. Sure, it's use both in mathematical expressions and in JavaScript expressions, but we're not talking about maths here. – Bergi Oct 02 '21 at 15:00
0

This is my own Answer after reading Answers by @Ben and @Bergi. My comments to respond to them under their Answers will not be sufficient in terms of the complexity and readability to readers, and editing the Question will also have the same problem for the amount of the document. (the previous duplicated section has been removed from the Question and moved here)

My response to Answer by @Bergi

Actually, this is the best explanation I've ever seen to illustrate what is the Hack pipe and how it works.


lhs |> rhs

is syntactic sugar for

(% => rhs)(lhs)

Very concise and great work! (The TC39-hack-pipe-proposal readme.md should add his explanation) I truly appreciate your great answer and sincerely will upvote it.

Having said that, here is my take regarding your insight and my own question - Does the Hack-style pipe operator |> take precedence over grouping operator ( ) in order of operations in JavaScript?

I believe now we share the common ground, and you say:

In particular, when you write 1 |> (log(f(%) |> g(%))); you cannot substitute the subexpression (log(f(%) |> g(%))) with 3.

Why not? Of course you have explained the reason I cannot substitute the subexpression, based on this; syntactic sugar System


lhs |> rhs

is syntactic sugar for

(% => rhs)(lhs)

and here is the tricky part. For some reason, you (@bergi) take this syntactic sugar System into account as a premise.

However, the fact is the syntactic sugar is a freehand creation, and there is no authority background. It's just an idea by someone.

On the other hand, my question is based on very common principle of mathematics.

  • Grouping parentheses mean the same thing in Haskell as they do in high school mathematics. They group a sub-expression into a single term. This is also what they mean in JavaScript.

  • think of evaluation order as a totally separate concept than grouping.

quoted from @Ben's answer: Does the functionality of Grouping operator () in JavaScript differ from Haskell or other programming languages?

In other words, my principle to use Grouping operator ( ) in JavaScript

The grouping operator ( ) controls the precedence of evaluation in expressions.

in the same manner as high-school-mathematics is refused by an idea of the syntactic sugar.

Observing such a situation, the question arises:

Does the Hack-style pipe operator |> take precedence over grouping operator ( ) in order of operations in JavaScript?

What do you think?

In fact, you say I have a confusion, and the fact is not. I'm not confused at all, and I think you are confused.

To me, if the creation of the system as the syntactic sugar is not compatible with the general usage of the grouping ( ), it's a failure work, and the community should not accept such a thing.

KenSmooth
  • 275
  • 1
  • 10
  • "and there is no authority background" there is. The point of syntactic sugar is that the original syntax and the sugar exhibit the same behavior according to the rules of the language. – Jonas Wilms Oct 07 '21 at 07:44
  • "I think you are confused" such commentary is off-toic for StackOverflow and does not support your goal of getting an answer to your question. – Jonas Wilms Oct 07 '21 at 07:46