Exponentiation operator precedence

# Jason Orendorff (7 years ago)

In math, -x² is -(x²), not (-x)². But as proposed for JS, -x**2 is (-x)**2.

PHP, Python, Haskell, and D side with the traditional algebraic notation, against JS. Here's PHP:

$ php -r 'print(-2 ** 2);'
-4

Python:

>>> -2 ** 2
-4

Haskell:

Prelude> -2 ^ 2
-4

The D grammar: dlang.org/grammar.html#UnaryExpression

Let's switch.

Another case to think about is -2 ** -2. In Haskell, that's a syntax error, but the other three languages all treat it the same way, so maybe JS should follow suit.

# Waldemar Horwat (7 years ago)

On 08/24/2015 10:08, Jason Orendorff wrote:

In math, -x² is -(x²), not (-x)². But as proposed for JS, -x**2 is (-x)**2.

PHP, Python, Haskell, and D side with the traditional algebraic notation, against JS. Here's PHP:

 $ php -r 'print(-2 ** 2);'
 -4

Python:

 >>> -2 ** 2
 -4

Haskell:

 Prelude> -2 ^ 2
 -4

The D grammar: dlang.org/grammar.html#UnaryExpression

Let's switch.

Let's not. As I said at the last meeting, making ** bind tighter than unary operators would break x**-2. And making it sometimes tighter and sometimes looser would be too confusing and lead to other opportunities for precedence inversion.

 Waldemar
# Jason Orendorff (7 years ago)

On Mon, Aug 24, 2015 at 5:45 PM, Waldemar Horwat <waldemar at google.com> wrote:

Let's not. As I said at the last meeting, making ** bind tighter than unary operators would break x**-2. And making it sometimes tighter and sometimes looser would be too confusing and lead to other opportunities for precedence inversion.

Don't you think having -x**2 mean the same thing as x**2 is more confusing? It seems like it will cause problems for the exact programmers we are trying to help with this feature.

What you're describing as "sometimes tighter and sometimes looser" I would call "the same precedence". It's even easier to specify than the current proposal:

UnaryExpression : PostfixExpression ** UnaryExpression

An expression using both ** and unary - is then parsed right-associatively:

-a ** -b ** -c ** -d
means -(a ** (-(b ** (-(c ** (-d))))))
# Daniel Ehrenberg (7 years ago)

On Mon, Aug 24, 2015 at 3:45 PM, Waldemar Horwat <waldemar at google.com> wrote:

On 08/24/2015 10:08, Jason Orendorff wrote:

In math, -x² is -(x²), not (-x)². But as proposed for JS, -x**2 is (-x)**2.

PHP, Python, Haskell, and D side with the traditional algebraic notation, against JS. Here's PHP:

 $ php -r 'print(-2 ** 2);'
 -4

Python:

 >>> -2 ** 2
 -4

Haskell:

 Prelude> -2 ^ 2
 -4

The D grammar: dlang.org/grammar.html#UnaryExpression

Let's switch.

Let's not. As I said at the last meeting, making ** bind tighter than unary operators would break x**-2. And making it sometimes tighter and sometimes looser would be too confusing and lead to other opportunities for precedence inversion.

Waldemar

Agreed that the precedence should not bind sometimes tighter and sometimes looser is problematic.

I think following the way other languages solve the same problem is more important for minimizing user surprise than any particular expression looking especially good. Looking kinda similar to other languages in surface syntax has been a major advantage of JS all along.

Dan

# Waldemar Horwat (7 years ago)

On 08/24/2015 17:24, Jason Orendorff wrote:

On Mon, Aug 24, 2015 at 5:45 PM, Waldemar Horwat <waldemar at google.com> wrote:

Let's not. As I said at the last meeting, making ** bind tighter than unary operators would break x**-2. And making it sometimes tighter and sometimes looser would be too confusing and lead to other opportunities for precedence inversion.

Don't you think having -x**2 mean the same thing as x**2 is more confusing? It seems like it will cause problems for the exact programmers we are trying to help with this feature.

What you're describing as "sometimes tighter and sometimes looser" I would call "the same precedence". It's even easier to specify than the current proposal:

 UnaryExpression : PostfixExpression ** UnaryExpression

An expression using both ** and unary - is then parsed right-associatively:

 -a ** -b ** -c ** -d
 means -(a ** (-(b ** (-(c ** (-d))))))

That has different right and left precedence and is probably the closest to the mathematical intent. However, it does carry other surprises. What does each of the following do?

++x ** y; x++ ** y; x ** ++y; x ** y++;

 Waldemar
# Jason Orendorff (7 years ago)

On Mon, Aug 24, 2015 at 7:24 PM, Jason Orendorff <jason.orendorff at gmail.com> wrote:

What you're describing as "sometimes tighter and sometimes looser" I would call "the same precedence". It's even easier to specify than the current proposal:

UnaryExpression : PostfixExpression ** UnaryExpression

P.S. Admittedly it might be a good idea to rename "UnaryExpression" if we put a binary operator in there.

# Jason Orendorff (7 years ago)

On Mon, Aug 24, 2015 at 8:10 PM, Waldemar Horwat <waldemar at google.com> wrote:

That has different right and left precedence and is probably the closest to the mathematical intent.

Not to quibble, but I do want to understand:

UnaryExpression : PostfixExpression ** UnaryExpression

AdditiveExpression : AdditiveExpression + MultiplicativeExpression

We don't say binary + has different left and right precedence, right? What's different with **, apart from being right-associative?

However, it does carry other surprises. What does each of the following do?

Interesting:

++x ** y;  // early error: invalid operand for ++
    (because it's parsed as `++(x ** y)`)
x++ ** y;  // parses as expected: (x++) ** y
x ** ++y;  // parses as expected: x ** (++y)
x ** y++;  // parses as expected: x ** (y++)

I'm OK with this. It should be rarer in practice than -x**2, and at least the language won't silently assign a surprising meaning to user code.

It could be made to parse as (++x) ** y by changing prefix ++ and -- to be higher precedence than the other prefix operators. I don't think this would break any existing programs, because combinations like ++typeof 3 are already early errors. PHP apparently does something like this:

$ php -r '$x = 3; print(++$x**2 . "\n"); print($x . "\n");'
16
4

D doesn't. Python and Haskell don't have ++/--.

# Mark S. Miller (7 years ago)

I think we should drop the feature. Given the conflict between

  • the history of ** in other languages,
  • the general pattern that unary binds tighter than binary

any solution at this point will confuse many people. These confusions will not result in a confusing static rejection but in runtime behavior that sometimes violates expectations. I was all for adding ** to the language when it did not have these problems. But it does. The minor convenience it adds is not worth these costs. By contrast, no one is confused about the parsing of calls to Math.pow.

When in doubt, leave it out.

# Allen Wirfs-Brock (7 years ago)

On Aug 25, 2015, at 8:11 AM, Mark S. Miller wrote:

I think we should drop the feature. Given the conflict between

  • the history of ** in other languages,
  • the general pattern that unary binds tighter than binary

any solution at this point will confuse many people. These confusions will not result in a confusing static rejection but in runtime behavior that sometimes violates expectations. I was all for adding ** to the language when it did not have these problems. But it does. The minor convenience it adds is not worth these costs. By contrast, no one is confused about the parsing of calls to Math.pow.

When in doubt, leave it out.

I've had the same reaction to this recent thread. It seems like the functional form (Math.pow) is a much less confusing formulation for use in JS expressions.

It's interesting to note that while early "algebraic languages" such as FORTRAN, Algol 60, BASIC, PL/I all had exponentiation operators the next couple generations of similar languages including Pascal, C, C++, Java, and C# do not. Each of those languages could have had an exponentiation operator but choose to exclude it.

The utility of an exponentiation operation may simply not be worth the complexity and potential for confusion it introduces into a language with a rich set of operators.

# Bill Frantz (7 years ago)

On 8/25/15 at 8:11 AM, erights at google.com (Mark S. Miller) wrote:

any solution at this point will confuse many people. These confusions will not result in a confusing static rejection but in runtime behavior that sometimes violates expectations.

It might be worthwhile flagging as errors constructs which cause frequent confusion. For example, I have been programming in C and C like languages for over 25 years. I still occasionally shoot myself in the foot combining bitwise logical operations and equality tests. If the language forced me to put int parenthesis, I would have been caught at compile time. While I try to put them in as a mater of good programming style, sometimes I forget.

However, there are probably legacy issues.

Cheers - Bill


Bill Frantz | If you want total security, go to prison. There you're 408-356-8506 | fed, clothed, given medical care and so on. The only www.pwpconsult.com | thing lacking is freedom. - Dwight D. Eisenhower

# Thomas (7 years ago)

This may be of some relevance: en.wikipedia.org/wiki/Order_of_operations#Special_cases

Exponentiation is clearly exponentially more complicated in terms of order of operations and precedence than other operators if the desire is to follow 'standard maths'. Otherwise, making Exponentiation behave differently in JavaScript to how it does elsewhere is going to be a constant source of bugs and needing parentheses just to get the desire behaviour negates the whole point of making it an operator vs a function.

Thomas

# Isiah Meadows (7 years ago)

So far, I like the idea of exponentiation having identical precedence to unary +/-, and lower than the increment operators. That sounds like it should work pretty well.

x ** y // x^y
-x ** y // -(x^y)
x ** -y // x^(-y)

++x ** y === (++x) ** y
x++ ** y === (x++) ** y
x ** ++y === x ** (++y)
x ** y++ === x ** (y++)
# Claude Pache (7 years ago)

I think the following grammar could work. Replace the current (ES2015) PostfixExpression production with:

IncrementExpression:
    LeftHandSideExpression
    LeftHandSideExpression [no LineTerminator here] ++
    LeftHandSideExpression [no LineTerminator here] --
    ++ LeftHandSideExpression
    -- LeftHandSideExpression

And define UnaryExpression as:

UnaryExpression:
    IncrementExpression    
    delete UnaryExpression
    void UnaryExpression
    typeof UnaryExpression
    ++ UnaryExpression
    + UnaryExpression
    -- UnaryExpression
    - UnaryExpression
    ~ UnaryExpression
    ! UnaryExpression
    IncrementExpression ** UnaryExpression

where the following production (which exists only to avoid to confusingly interpret, e.g., ++x++ as + +x++):

UnaryExpression:
    ++ UnaryExpression
    -- UnaryExpression

yields a static SyntaxError (or a static ReferenceError if we want to be 100% compatible ES2015).

That way, we have the following expected behaviour:

  • in/decrement operators bind most tightly;
  • unary and exponentiation operators are applied from right to left.
# Isiah Meadows (7 years ago)

I like this. It works very well.

# Mark S. Miller (7 years ago)

It does not work as well as simply omitting ** entirely.

# Mark S. Miller (7 years ago)

It also does not work. x ** y ** z, if we allow it at all, must be right associative. It must parse as x ** (y ** z).

# Florian Bösch (7 years ago)

I'm a python user and I dislike using **, it just becomes rather noisy.

Expressing formulas in text based programming languages has always been kind of a drag. On the other hand, often the mathematical expression of a formula would be quite inefficient because they lack the ability to keep temporary results in some variable. Picking a formula apart to isolate those temporaries the expressiveness vanishes naturally.

# Claude Pache (7 years ago)

Le 25 août 2015 à 19:25, Mark S. Miller <erights at google.com> a écrit :

It also does not work. x ** y ** z, if we allow it at all, must be right associative. It must parse as x ** (y ** z).

Unless I missed something, it is right-associative.

# Mark S. Miller (7 years ago)

Sorry. I just has another look at your proposed grammar and you are correct.

# Rick Waldron (7 years ago)

On Tue, Aug 25, 2015 at 11:12 AM Mark S. Miller <erights at google.com> wrote:

I think we should drop the feature. Given the conflict between

  • the history of ** in other languages,
  • the general pattern that unary binds tighter than binary

any solution at this point will confuse many people. These confusions will not result in a confusing static rejection but in runtime behavior that sometimes violates expectations. I was all for adding ** to the language when it did not have these problems. But it does. The minor convenience it adds is not worth these costs. By contrast, no one is confused about the parsing of calls to Math.pow.

Math.pow has exactly the same semantics as the current ** . I'd like the opportunity to work with Claude and Brian to resolve the grammar issue before just tossing it out.

Thanks.

# Waldemar Horwat (7 years ago)

On 08/25/2015 09:38, Claude Pache wrote:

I think the following grammar could work. Replace the current (ES2015) PostfixExpression production with:

IncrementExpression:
     LeftHandSideExpression
     LeftHandSideExpression [no LineTerminator here] ++
     LeftHandSideExpression [no LineTerminator here] --
     ++ LeftHandSideExpression
     -- LeftHandSideExpression

And define UnaryExpression as:

UnaryExpression:
     IncrementExpression
     delete UnaryExpression
     void UnaryExpression
     typeof UnaryExpression
     ++ UnaryExpression
     + UnaryExpression
     -- UnaryExpression
     - UnaryExpression
     ~ UnaryExpression
     ! UnaryExpression
     IncrementExpression ** UnaryExpression

The above is not a valid grammar. For example, parsing ++x leads to a reduce-reduce conflict, where the ++ can come from either a UnaryExpression or an IncrementExpression.

where the following production (which exists only to avoid to confusingly interpret, e.g., ++x++ as + +x++):

That makes no sense. ++ is a lexical token. The lexer always greedily bites off the largest token it can find, even if that leads to a parser error later. The parser does not backtrack into the lexer to look for alternate lexings. For example,

x +++++ y;

is a syntax error because it's greedily lexed as:

x ++ ++ + y;

The parser does not backtrack into the lexer to look for other possible lexings such as:

x ++ + ++ y;

UnaryExpression:
     ++ UnaryExpression
     -- UnaryExpression

yields a static SyntaxError (or a static ReferenceError if we want to be 100% compatible ES2015).

This is a problem. It makes ++x into a static SyntaxError because x is a UnaryExpression.

If you got rid of these two ++ and -- productions in UnaryExpression, that would solve that problem (and I think make the grammar valid).

 Waldemar
# Claude Pache (7 years ago)

Le 25 août 2015 à 03:22, Jason Orendorff <jason.orendorff at gmail.com> a écrit :

On Mon, Aug 24, 2015 at 7:24 PM, Jason Orendorff <jason.orendorff at gmail.com> P.S. Admittedly it might be a good idea to rename "UnaryExpression" if we put a binary operator in there.

-j

"RightAssociativeExpression"?

# Mark S. Miller (7 years ago)

When the costs were minor, it was ok that the benefits were minor. Given significant costs, we need to ask:

Why do we need ** ? What great benefit does it provide? If nothing compelling, then this proposal has lost consensus.

# Isiah Meadows (7 years ago)

I would have to agree with Mark on this one. I have yet to find anything compelling for an exponentiation operator. I know multiple things that would be more interesting, such as the proposed bind operator.

Several languages have it, but I don't think "it's in several other languages, so it has to be in this language" is enough to merit an addition to the language. And in my honest opinion, it doesn't look nice, either.

# Claude Pache (7 years ago)

Le 26 août 2015 à 00:43, Mark S. Miller <erights at google.com> a écrit :

When the costs were minor, it was ok that the benefits were minor. Given significant costs, we need to ask:

While I don't have a strong opinion about the cost of the proposed modified grammar, I protest that the cost of the previous version wasn't anything near minor (although it was probably an oversight): having -x**y producing (literally) the opposite result of what is expected, and even only half of the time, is a high cost in terms of bugs produced and debugging man-hours lost.

# Mark S. Miller (7 years ago)

I completely agree. My "When the costs were minor" refers to when we were not yet aware of the conflict.

# Jason Orendorff (7 years ago)

On Tue, Aug 25, 2015 at 5:43 PM, Mark S. Miller <erights at google.com> wrote:

When the costs were minor, it was ok that the benefits were minor.

The costs will probably still be minor if we just let Rick look at it and revise the proposal.

What has happened here is

  • upon implementing the feature, we noticed a problem
  • we thought through it together and found possible solutions
  • we found other languages already use these solutions

This seems like less turbulence than average for a new ES feature, even a minor one. Considering dropping the feature seems premature.

# Mark S. Miller (7 years ago)

I don't get it. The conflict between

  • the history of ** in other languages,
  • the general pattern that unary binds tighter than binary

seems unresolvable. By the first bullet, -2 ** 2 would be -4. By the second, it would be 4. Either answer will surprise too many programmers. By contrast, no one is confused by either -Math.pow(2, 2) or Math.pow(-2, 2).

# Brendan Eich (7 years ago)

Yehuda Katz cited an acronym taught when he was a wee lad learning algebra: PEMDAS (Parentheses, Exponentiation, Multiplication/Dviistion, Addition/Subtraction). Who else learned this?

There's nothing sacrosanct about binary precedence being generally lower than unary. Consider the property access operators in JS. But the precedent to which all cited languages bow is Math and that's what programmers (mostly) study. I think you are making too much out of the local -x ** y case in light of this global argument.

# Thomas (7 years ago)

There's still the issue of exponentiation being right-associative. Unless ** becomes an operator which behaves differently as to how it would in a high school maths class, we're at an impasse.

That said, ^ is usually the operator used for exponentiation outside programming languages when you need to express an equation in text. It could be made explicit that ** is a variant on 'exponentiation', but then maybe things are deviating from being useful.

Thomas

# Jason Orendorff (7 years ago)

On Wed, Aug 26, 2015 at 11:09 AM, Mark S. Miller <erights at google.com> wrote:

I don't get it. The conflict between

  • the history of ** in other languages,
  • the general pattern that unary binds tighter than binary

seems unresolvable. By the first bullet, -2 ** 2 would be -4. By the second, it would be 4. Either answer will surprise too many programmers.

I just think the danger is not so great. Who's really going to be surprised that -x**2 is negative? This follows not only other mainstream programming languages but a mathematical notation that has been in common use for hundreds of years and is taught to every high school algebra student.

# Jason Orendorff (7 years ago)

On Wed, Aug 26, 2015 at 1:03 PM, Thomas <thomasjamesfoster at bigpond.com> wrote:

There's still the issue of exponentiation being right-associative. Unless ** becomes an operator which behaves differently as to how it would in a high school maths class, we're at an impasse.

I'm not sure I follow. Exponentiation is right-associative in math, in the current ** proposal, and in every suggested update to it.

# Allen Wirfs-Brock (7 years ago)

On Aug 26, 2015, at 9:09 AM, Mark S. Miller wrote:

I don't get it. The conflict between

  • the history of ** in other languages,
  • the general pattern that unary binds tighter than binary

seems unresolvable. By the first bullet, -2 ** 2 would be -4. By the second, it would be 4. Either answer will surprise too many programmers. By contrast, no one is confused by either -Math.pow(2, 2) or Math.pow(-2, 2).

An of course the history includes many very popular languages that chose to not include an exponentiation operator.

This is really a cost-benefits issue. Several of us have identified costs (complexity, cognitive, pedagogical, error hazards, etc.). What are the benefits of having such an operator? Are they sufficient to offset the cost of having it.

# Alexander Jones (7 years ago)

Exponentiation is written in conventional mathematics as if it were a postfix unary operator, parameterised by a value written in superscript. IMO this puts it in a whole different class to binary operators where both operands are written equally. I don't see a ** b ** c as a good reflection of mathematical convention.

Number.prototype.pow, on the other hand, would be fine.

# Waldemar Horwat (7 years ago)

On 08/26/2015 09:09, Mark S. Miller wrote:

I don't get it. The conflict between

  • the history of ** in other languages,
  • the general pattern that unary binds tighter than binary

seems unresolvable. By the first bullet, -2 ** 2 would be -4. By the second, it would be 4. Either answer will surprise too many programmers. By contrast, no one is confused by either -Math.pow(2, 2) or Math.pow(-2, 2).

The grammar concerns have been resolved nicely upthread, so I'm not sure what your objection is. The costs are no more significant than in the original proposal. ** now has the same precedence as unary operators and weaker than the increment operators, which matches what most other languages that support exponentiation do.

There is precedence for unary operators not always binding tighter than binary. yield 3+4 is yield(3+4), not (yield 3)+4.

 Waldemar
# Mark S. Miller (7 years ago)

On Wed, Aug 26, 2015 at 2:55 PM, Waldemar Horwat <waldemar at google.com>

wrote:

On 08/26/2015 09:09, Mark S. Miller wrote:

I don't get it. The conflict between

  • the history of ** in other languages,
  • the general pattern that unary binds tighter than binary

seems unresolvable. By the first bullet, -2 ** 2 would be -4. By the second, it would be 4. Either answer will surprise too many programmers. By contrast, no one is confused by either -Math.pow(2, 2) or Math.pow(-2, 2).

The grammar concerns have been resolved nicely upthread, so I'm not sure what your objection is. The costs are no more significant than in the original proposal. ** now has the same precedence as unary operators and weaker than the increment operators, which matches what most other languages that support exponentiation do.

There is precedence for unary operators not always binding tighter than binary. yield 3+4 is yield(3+4), not (yield 3)+4.

The force of that precedent is indeed what my objection is. The "yield" counter-example is interesting, but "yield" is an identifier not an operator symbol, and so does not as clearly fall within or shape operator expectations.

If someone explains a compelling need for ** I would find that interesting. But until then...

# Bergi (7 years ago)

Alexander Jones schrieb:

Exponentiation is written in conventional mathematics as if it were a postfix unary operator, parameterised by a value written in superscript. IMO this puts it in a whole different class to binary operators where both operands are written equally. I don't see a ** b ** c as a good reflection of mathematical convention.

I disagree. When in maths we write x <sup> y <sup> z </sup></sup>, we

mean x ^ (y ^ z). Which is exactly what x ** y ** z will do. And no, we never write x <sup>y</sup> <sup>z</sup>, if we wanted to

express that we'd write x <sup>y * z</sup> (or often enough, with

implicit multiplication, i.e. no * operator: x <sup>y z</sup>).

If we'd want to express that in JS, it would by x ** (y * z).

Number.prototype.pow, on the other hand, would be fine.

You don't mean to use it like x.pow(y).pow(z), do you? Sure, a function or method invocation is always explicit with parenthesis. But that's no improvement over the current Math.pow.

Bergi

# Waldemar Horwat (7 years ago)

On 08/26/2015 15:08, Mark S. Miller wrote:

The force of that precedent is indeed what my objection is. The "yield" counter-example is interesting, but "yield" is an identifier not an operator symbol, and so does not as clearly fall within or shape operator expectations.

If someone explains a compelling need for ** I would find that interesting. But until then...

** is a convenience, and that's the wrong criterion to apply here. If it were, then we wouldn't have useful conveniences like Math.cosh or arrow functions.

I'd rather read

ax**3 + bx**2 + c*x + d

than

aMath.pow(x, 3) + bMath.pow(x, 2) + c*x + d

 Waldemar
# Mark S. Miller (7 years ago)

On Wed, Aug 26, 2015 at 6:19 PM, Waldemar Horwat <waldemar at google.com>

wrote:

On 08/26/2015 15:08, Mark S. Miller wrote:

The force of that precedent is indeed what my objection is. The "yield" counter-example is interesting, but "yield" is an identifier not an operator symbol, and so does not as clearly fall within or shape operator expectations.

If someone explains a compelling need for ** I would find that interesting. But until then...

** is a convenience, and that's the wrong criterion to apply here. If it were, then we wouldn't have useful conveniences like Math.cosh or arrow functions.

I'd rather read

ax**3 + bx**2 + c*x + d

than

aMath.pow(x, 3) + bMath.pow(x, 2) + c*x + d

Ok, we have a benefit to evaluate. Brevity. With the example contrast between

a*x**3 + b*x**2 + c*x + d

and

a*Math.pow(x, 3) + b*Math.pow(x, 2) + c*x + d

Let's also apply Alexander's suggestion

a*x.pow(3) + b*x.pow(2) + c*x + d

To help us compare for brevity, and because I'm too lazy to count, I'm sending it in a fixed width font.

# Jordan Harband (7 years ago)

Is there also perhaps a potential performance/static analysis benefit in having it be syntax, rather than a builtin function? Math.pow can be overwritten, and varies per-realm, but ** can not and does not.

# Isiah Meadows (7 years ago)

Not much, as far as I can tell. Engines do usually lower this, and redo the whole object when the shape changes, or an intrinsic no longer applies. V8 has a MathPow intrinsic, and I believe SpiderMonkey has similar.

# Niloy Mondal (7 years ago)

x ** y ** z is easier to read/write than x.pow(y.pow(z))

# Bill Frantz (7 years ago)

On 8/27/15 at 11:51 PM, niloy.mondal84 at gmail.com (Niloy Mondal) wrote:

x ** y ** z is easier to read/write than x.pow(y.pow(z))

As a language guru, you know which operation will be performed first. As Joe programmer, I don't and I would need to write it as x ** (y ** z).

With some operations, like +, the order doesn't matter and x + y

  • z is not confusing.

Cheers - Bill


Bill Frantz |"We used to quip that "password" is the most common 408-356-8506 | password. Now it's 'password1.' Who said users haven't www.pwpconsult.com | learned anything about security?" -- Bruce Schneier

# Fabrício Matté (7 years ago)

You don't need to be a language guru to know which operation will be performed first, you can (and should) check an operator precedence and associativity table such as MDN's developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence . Knowing operator precedence and associativity is very important when reading code written by others, and many projects use the "no unnecessary parens" linting rule.

# Kevin Smith (7 years ago)

x ** y ** z is easier to read/write than x.pow(y.pow(z))

That might be cherry picking. Trying to make up something a little more complex:

a**b * -c**d + e

Math.pow(a, b) * -Math.pow(c, d) + e

a.pow(b) * -c.pow(d) + e

I don't have strong feelings on this issue, but the third option looks pretty good to me. If we had some form of pipelining syntax (yet to be proposed), then we could have:

let { pow } = Math;
a->pow(b) * -c->pow(d) + e;
# Jason Orendorff (7 years ago)

On Thu, Aug 27, 2015 at 9:04 AM, Kevin Smith <zenparsing at gmail.com> wrote:

a**b * -c**d + e

I don't think people use unary - like this very often. It's nothing to do with exponentiation. You don't see people write:

a * -c + e

because the right-side-up way to say that is:

e - a * c
# Kevin Smith (7 years ago)

because the right-side-up way to say that is:

e - a * c

Yeah, I was waiting for someone to point that out, after I hit send. : ) I should spend more time setting up a better examples...

# Dean Tribble (7 years ago)

Ideally syntax proposals should include some frequency information to motivate any change. Is there an easy search to estimate the frequency of Math.pow? In my application codebase (financial app with only modest JS use), there are very few uses, and there are as many uses of Math.sin as there are of Math.pow.

Anecdotally, my eyes caught on: "-Math.pow(2,-10*a/1)" (from a charting library) which makes me not want to have to review code where I'm worried about the precedence of exponentiation.

# Steve Fink (7 years ago)

On 08/27/2015 09:25 AM, Dean Tribble wrote:

Ideally syntax proposals should include some frequency information to motivate any change. Is there an easy search to estimate the frequency of Math.pow? In my application codebase (financial app with only modest JS use), there are very few uses, and there are as many uses of Math.sin as there are of Math.pow.

Frequency relative to what, though? If code that does nontrivial math is a very small proportion of total JS code, and yet the exponentiation operator makes that code much more readable, then what is the conclusion? I would argue that ** precedence confusion is irrelevant to code that isn't going to use Math.pow in the first place. So it's a question of whether ** is a big enough readability win in code that computes exponents.

Anecdotally, my eyes caught on: "-Math.pow(2,-10*a/1)" (from a charting library) which makes me not want to have to review code where I'm worried about the precedence of exponentiation.

I'd have to write that out: -2**(-10*a/1). That doesn't seem too bad.

For myself, I do very much prefer Math.sqrt(a2 + b2) to Math.sqrt(Math.pow(a, 2) + Math.pow(b, 2)). The verbosity and uneven density of notation is really bothersome -- for any equation like the second one, I guarantee that I'll rewrite it on paper to figure out what it's saying. (Ok, maybe not for that specific formula, but even there I'll mentally render it.) I would not need to do so with the first one. Jumping between prefix and infix is jarring.

Then again, I could make the same argument for Math.sqrt(a2 + b2) vs (a2 + b2) ** 0.5. And I don't like the second one much. But people don't interchange those when handwriting formulas, either.

Math.sqrt(a.pow(2) + b.pow(2)) is an interesting middle point. I initially thought it struck the right balance, but seeing it written out, it still looks far inferior to me.

A more complex example might help:

a * (b - a)**(x - 1/2 * (b - a)**2)

vs

a * Math.pow(b - a, x - 1/2 * Math.pow(b - a, 2))

vs

a * (b - a).pow(x - 1/2 * (b - a).pow(2))

For me, the middle one is a mess. I can't make sense of it, and I can't spot the common (b - a) expression at all. The first one is as readable as such formulas ever are when written out with ASCII text. The third one is somewhere in between. I can see the common (b - a), and perhaps if I got more used to seeing .pow I could mentally make use of it without writing it on paper, but for now I cannot. Part of the problem is that I can easily translate "x**2" into "x squared", but "x.pow(2)" is raising x to the power of 2.

# Nathan White (7 years ago)

Anecdotal evidence via a quick github search search?l=JavaScript&p=1&q=Math.pow+language%3AJavaScript+extension%3Ajs&ref=advsearch&type=Code&utf8=✓

A significant number of the usages are unit tests. Many others are repeats from popular libraries like D3.

The most extreme use case I could find: y4ashida/ika/blob/master/js/culc_dence.js y4ashida/ika/blob/master/js/culc_dence.js

# Ethan Resnick (7 years ago)

Long-time esdiscuss lurker; hopefully this perspective is helpful.

I think the problem here is that traditional mathematic notation uses visual cues to imply precedence that JS can't take advantage of. When -3 ** 2 is written out on paper, the 2 is very clearly grouped visually with the 3. In fact, the superscript almost makes the 2 feel like an appendage of the 3. That makes it more natural to read it as two items: the negative sign, and (3 ** 2).

By contrast, when (-3 ** 2) is written out in code, the negative sign is way closer visually to the 3 than the 2 is, so I find myself instinctively pulling out a "-3" first and reading the expression as (-3)**2.

Treating -3 ** 2 as -(3 ** 2) seems technologically possible and mathematically sensible, but also like it's going against the grain of the human visual system. I think that's at least one reason to value the "binary precedence should be lower than unary" principle.

The Number.prototype.pow approach might be an improvement, as it has the effect of mandating parentheses on some cases that might otherwise be confusing (e.g. x ** y ** z) and it offers most of the conciseness of **. But -x.pow(2) still feels unpredictable to me as an everyday programmer switching between languages. (Also, pow() requires an extra set of parentheses if I want to operate on a literal.)

Maybe it's ok if the operator surprises some people in some cases, and the guidance will just become to use parentheses if you're unsure. That occasional uncertainty for the vast majority of JS programmers that aren't doing much exponentiation might be worth it if ** makes a minority of JS programmers much more productive. I don't know.

# Steve Fink (7 years ago)

On 08/27/2015 11:20 AM, Ethan Resnick wrote:

Long-time esdiscuss lurker; hopefully this perspective is helpful.

I think the problem here is that traditional mathematic notation uses visual cues to imply precedence that JS can't take advantage of. When -3 ** 2 is written out on paper, the 2 is very clearly grouped visually with the 3. In fact, the superscript almost makes the 2 feel like an appendage of the 3. That makes it more natural to read it as two items: the negative sign, and (3 ** 2).

By contrast, when (-3 ** 2) is written out in code, the negative sign is way closer visually to the 3 than the 2 is, so I find myself instinctively pulling out a "-3" first and reading the expression as (-3)**2.

If we're making ** bind tighter than unary -, then I would hope it would be written -3**2, not -3 ** 2. The latter is indeed deceptive.

For me, xyz is rare enough that I don't really care if ** is right associative or nonassociative. Parentheses are part of the cost you have to pay for rendering things as plain text -- and yet, I see no reason not to make xyz just do the right thing.

# Alexander Jones (7 years ago)

Ethan is making my point far better than I did, and I agree completely about the issue of unary operators visually appearing more tightly bound than binary operators.

At this point it seems fair to at least acknowledge the prospect of significant whitespace.

-x**2 === -(x ** 2)
-x ** 2 === (-x) ** 2
# Waldemar Horwat (7 years ago)

On 08/27/2015 11:58, Alexander Jones wrote:

Ethan is making my point far better than I did, and I agree completely about the issue of unary operators visually appearing more tightly bound than binary operators.

At this point it seems fair to at least acknowledge the prospect of significant whitespace.

-x**2 === -(x ** 2)
-x ** 2 === (-x) ** 2

Take a look at the Fortress language ☺. But that one benefits from operators and syntax not limited to ASCII.

 Waldemar
# Joe Gibbs Politz (7 years ago)

On Thu, Aug 27, 2015 at 2:58 PM, Alexander Jones <alex at weej.com> wrote:

Ethan is making my point far better than I did, and I agree completely about the issue of unary operators visually appearing more tightly bound than binary operators.

At this point it seems fair to at least acknowledge the prospect of significant whitespace.

-x**2 === -(x ** 2)
-x ** 2 === (-x) ** 2

One kind of cost that I haven't seen mentioned (and is relevant re:whitespace) is the impact on minifiers and other tools that use JS as output, which have to deal with operator precedence/whitespace rules in quite complicated ways. There's a nice recent piece about precedence bugs in a minifier:

zyan.scripts.mit.edu/blog/backdooring-js

Making the creation and maintenance of systems like minifiers harder is a real cost worth bearing in mind when updating already-subtle existing rules.

# Jason Orendorff (7 years ago)

Don't rely on github searches to turn up representative examples. It doesn't work that well. Here's my educated guess as to how ** will be used.

The most common use will be to square numbers.

a²
a**2
Math.pow(a, 2)
a.pow(2)

Currently you might write a * a, which is kind of lame.

So where's the benefit? If this trivial thing is the most common use of exponentation, why bother?

The ability to look at an expression and understand it at a glance is a big deal.

x² + y² > limit
x**2 + y**2 > limit
Math.pow(x, 2) + Math.pow(y, 2) > limit
x.pow(2) + y.pow(2) > limit

A big big deal. It's the reason we have arithmetic operators in JS in the first place.

Exponentiation is common when computing easing functions, curves for graphics, interest, simulations, random stuff in games. Nth roots are fairly common too (apr**(1/12)). In all of these cases, the user is doing the same thing: translating a mathematical formula they wish to use from "math" to JS. It is not extra hard to translate such a formula using Math.pow(), but it is harder to read once you're done. You have to mentally translate back to "math".

# Brendan Eich (7 years ago)

Mark S. Miller wrote:

but "yield" is an identifier not an operator symbol, and so does not as clearly fall within or shape operator expectations.

delete typeof

These are high-precedence, same level as unary +/- etc. Unlike yield, which is at assignment operator level.

I'm simply questioning your "identifier not an operator symbol" razor here, when previously you argued "unary operators have higher precedence" without distinguishing the identifier-named unary ops of high precedence.

# Brendan Eich (7 years ago)

Not to worry, the significant whitespace prospect was (I trust) a warding-off spell. Good of Waldemar to mention Fortress, too.

JS, which as source is and will always be minified, indeed requires full-parsing minifiers, so one might naively still entertain the stated prospect. But it's a bad idea, since people minify (or just tidy by removing spaces) by hand. Keep warding off significant space!

# Isiah Meadows (7 years ago)

I agree with that completely.

This would also become a gotcha. I can see a lot of people down the road thinking -x ** 2 === -x**2, only to find that's not the case. It looks like it should, which will quickly lead to hard to find bugs. I think it's a terrible idea, but that's just my opinion.

# Brendan Eich (7 years ago)

Quick update from TC39 yesterday where Rick and I presented the Stage 3 Exponentiation Operator proposal:

rwaldron.github.io/exponentiation-operator

The current spec, revised to match precedent from all known programming languages that have exponentiation operators, binds

-x^y = -(x^y )

and not

-x^y = (-x)^y

as the original proposal specified. These examples use Math notation, but the proposed exponentiation operator is infix ** of course. And that's the source of trouble for this proposal.

The problem is, however rare unary minus before an exponentiation expression may be, the lack of superscript-with-smaller-font sugests that - binds tighter than **. And indeed apart from dot (a special form whose right operand must be a lexical identifier-name) and square brackets (which isn't an infix operator per se), unary operators bind tighter than binary in JS as in C and other C-derived languages.

Yehuda suggested that exponentiation was "cargo-culted" from Math into Fortran, and then into other languages, without considering the notational shift and the apparent precedence inversion. I don't know the history, but it's plausible, and numerics folks probably would not expect unary - to bind tighter than exponentiation. But JS programmers may see -x and especially -2 as a tighter expression, if not a single lexeme, than any expression joined by infix **.

We debated the options, which I think are four in number:

  1. Give up because the proposal has hit a "wall of confusion".

  2. Stick with the current spec,

  3. Go back to the old spec, which flouts precedent.

  4. Make unparenthesized exponentiation expression as operand of unary operators an early error.

I came up with (4) late in the day, and it didn't get a fair hearing. Before I wrote it up on the board, I asked for a straw poll (those can bite back by forcing people onto a bandwagon, as Dave Herman pointed out) on (1-3). The poll favored (2), with notable but minority positions for (1) and (3).

The grammar change needed for (4) is trivial. Instead of

UnaryExpression_[Yield] : IncrementExpression_[?Yield] delete UnaryExpression_[?Yield] void UnaryExpression_[?Yield] typeof UnaryExpression_[?Yield]

  • UnaryExpression_[?Yield]
  • UnaryExpression_[?Yield] ~ UnaryExpression_[?Yield] !UnaryExpression_[?Yield] IncrementExpression_[?Yield] ** UnaryExpression_[?Yield]

we factor to require parentheses around the last right-hand side if it is an operand of a unary operator:

UnaryExpression_[Yield] : SimpleUnaryExpression_[?Yield] IncrementExpression_[?Yield] ** UnaryExpression_[?Yield]

SimpleUnaryExpression_[Yield] : IncrementExpression_[?Yield] delete SimpleUnaryExpression_[?Yield] void SimpleUnaryExpression_[?Yield] typeof SimpleUnaryExpression_[?Yield]

  • SimpleUnaryExpression_[?Yield]
  • SimpleUnaryExpression_[?Yield] ~ SimpleUnaryExpression_[?Yield] !SimpleUnaryExpression_[?Yield]

(It would be nice to rename non-terminals like so: s/<UnaryExpression>/ExponentiationExpression/g;

s/<SimpleUnaryExpression>/UnaryExpression/g where <> are left and

right word boundaries -- but I'm leaving this out for now since it touches more of the spec and is merely a nominal change.)

Thus one may write

let z = K - x**y;

without having to parenthesize unduly, but one cannot write

let z = -x ** y;

The user is forced by an early error to write either (-x)y or -(xy).

The early error stops parsing with a thrown SyntaxError.

It seems to me (4) wins and rescues the proposal at stage 3 from suffering a loss of consensus. Comments welcome.

# Brendan Eich (7 years ago)

[Apologies for resending, trying to fix formatting of grammar excerpts. /be]

Quick update from TC39 yesterday where Rick and I presented the Stage 3 Exponentiation Operator proposal:

rwaldron.github.io/exponentiation-operator

The current spec, revised to match precedent from all known programming languages that have exponentiation operators, binds

-x^y = -(x^y )

and not

-x^y = (-x)^y

as the original proposal specified. These examples use Math notation, but the proposed exponentiation operator is infix ** of course. And that's the source of trouble for this proposal.

The problem is, however rare unary minus before an exponentiation expression may be, the lack of superscript-with-smaller-font sugests that - binds tighter than **. And indeed apart from dot (a special form whose right operand must be a lexical identifier-name) and square brackets (which isn't an infix operator per se), unary operators bind tighter than binary in JS as in C and other C-derived languages.

Yehuda suggested that exponentiation was "cargo-culted" from Math into Fortran, and then into other languages, without considering the notational shift and the apparent precedence inversion. I don't know the history, but it's plausible, and numerics folks probably would not expect unary - to bind tighter than exponentiation. But JS programmers may see -x and especially -2 as a tighter expression, if not a single lexeme, than any expression joined by infix **.

We debated the options, which I think are four in number:

  1. Give up because the proposal has hit a "wall of confusion".

  2. Stick with the current spec,

  3. Go back to the old spec, which flouts precedent.

  4. Make unparenthesized exponentiation expression as operand of unary operators an early error.

I came up with (4) late in the day, and it didn't get a fair hearing. Before I wrote it up on the board, I asked for a straw poll (those can bite back by forcing people onto a bandwagon, as Dave Herman pointed out) on (1-3). The poll favored (2), with notable but minority positions for (1) and (3).

The grammar change needed for (4) is trivial. Instead of

UnaryExpression_[Yield] : IncrementExpression_[?Yield] delete UnaryExpression_[?Yield] void UnaryExpression_[?Yield] typeof UnaryExpression_[?Yield] + UnaryExpression_[?Yield] - UnaryExpression_[?Yield] ~ UnaryExpression_[?Yield] !UnaryExpression_[?Yield] IncrementExpression_[?Yield] ** UnaryExpression_[?Yield]

we factor to require parentheses around the last right-hand side if it is an operand of a unary operator:

UnaryExpression_[Yield] : SimpleUnaryExpression_[?Yield] IncrementExpression_[?Yield] ** UnaryExpression_[?Yield]

SimpleUnaryExpression_[Yield] : IncrementExpression_[?Yield] delete SimpleUnaryExpression_[?Yield] void SimpleUnaryExpression_[?Yield] typeof SimpleUnaryExpression_[?Yield] + SimpleUnaryExpression_[?Yield] - SimpleUnaryExpression_[?Yield] ~ SimpleUnaryExpression_[?Yield] !SimpleUnaryExpression_[?Yield]

(It would be nice to rename non-terminals like so: s/<UnaryExpression>/ExponentiationExpression/g;

s/<SimpleUnaryExpression>/UnaryExpression/g where <> are left and

right word boundaries -- but I'm leaving this out for now since it touches more of the spec and is merely a nominal change.)

Thus one may write

let z = K - x**y;

without having to parenthesize unduly, but one cannot write

let z = -x ** y;

The user is forced by an early error to write either (-x)y or -(xy).

The early error stops parsing with a thrown SyntaxError.

It seems to me (4) wins and rescues the proposal at stage 3 from suffering a loss of consensus. Comments welcome.

# Brendan Eich (7 years ago)

Here's a nicely formatted jsbin version of my message:

jsbin.com/bihilaveda

Sorry about the mess, mail user agents (at least Postbox) and archive software do not like explicit indentation.

# Mark S. Miller (7 years ago)

I like #4. Normally in a situation like this I would still argue for #1. #4 is a complicated special case that breaks the normal pattern of operator precedence elsewhere in the language. The need for ** is not great enough to justify introducing a new special case for users to learn.

However, in this case, #4 is only technically complicated -- for those writing or reading spec docs like us. For normal users, the only complexity is a rarely encountered surprising static error. With a decent (and easy to generate) error message, these users will immediately know what they need to do to repair their program.

Significant programs are read much more than they are written. Both #2 and .#3 will lead many readers to misread programs. For programs that are not rejected, #4 is no more confusing than #1. Altogether, for readers, #4 is better than #1 because ** is more readable than Pow.

+1 on #4.

# Brendan Eich (7 years ago)

Even nicer:

jsbin.com/baquqokujo

I hate email.

# Andreas Rossberg (7 years ago)

On 24 September 2015 at 17:19, Brendan Eich <brendan at mozilla.org> wrote:

Even nicer:

jsbin.com/baquqokujo

I hate email.

You are holding it wrong.

# Mark S. Miller (7 years ago)

I won't try to guess where the rendering problem is, but see the attached screenshots. This is how I'm seeing your page on my Chrome and Firefox.

# Rick Waldron (7 years ago)

Thanks again to Brendan for taking time to write this up. And to Mark, thanks for reviewing this and expeditiously providing valuable feedback—it's greatly appreciated.

# Claude Pache (7 years ago)

Le 24 sept. 2015 à 16:11, Brendan Eich <brendan at mozilla.org> a écrit :

And indeed apart from dot (a special form whose right operand must be a lexical identifier-name) and square brackets (which isn't an infix operator per se), unary operators bind tighter than binary in JS as in C and other C-derived languages.

I just wonder why it is important that unary binds tighter? For instance, before I carefully studied the issue of this thread, I have never expected that unary minus binds tighter than binary multiplication operator in expressions like -2*x (although it does not matter in that case).

without having to parenthesize unduly, but one cannot write

let z = -x ** y;

The user is forced by an early error to write either (-x)y or -(xy).

In traditional math notation, when you mean (-x)**n, you write (-x)ⁿ with mandatory parentheses, so I don’t expect that many people will be tempted to miswrite it -x ** n.

Making the parentheses mandatory here will be somewhat annoying in perfectly reasonable expressions, where you usually don’t use parentheses in real math notation., like:

let s2 =  - x**2 - y**2 - z**2 +  t**2
# Mark S. Miller (7 years ago)

On Thu, Sep 24, 2015 at 11:08 AM, Claude Pache <claude.pache at gmail.com>

wrote:

Le 24 sept. 2015 à 16:11, Brendan Eich <brendan at mozilla.org> a écrit :

And indeed apart from dot (a special form whose right operand must be a lexical identifier-name) and square brackets (which isn't an infix operator per se), unary operators bind tighter than binary in JS as in C and other C-derived languages.

I just wonder why it is important that unary binds tighter? For instance, before I carefully studied the issue of this thread, I have never expected that unary minus binds tighter than binary

Before Jason pointed out the discrepancy:

  • all of us on the committee who were engaged with the proposal
  • including myself,
  • all those who reviewed the proposal,
  • and all those who implemented the proposal had the opposite naive expectation. That's the point. In the absence of learning about this case specifically, many people will be unpleasantly surprised by #2, and many by #3. Therefore #4 wins. (Actually, it just won ;).)
# Brendan Eich (7 years ago)

Right. It's confusing because (as opposed to Math), ** is not a superscripting operator whose typography suggests higher precedence than -, and this matters for the sign of the result when exponentiating.

Claude, the Math folks won't often need to negate the result, but when they do, they'll have to parenthesize. That's the price of the typographic shift and the precedence inversion that it suggests to many people.

# Domenic Denicola (7 years ago)

I object to #4. Disallowing perfectly reasonable math expressions (Claude's is a good example) makes this operator too surprising to include in the language.

# Brendan Eich (7 years ago)

Sez you! :-P

Seriously, the problem you are dismissing implicitly (bad form :-/) is the one we discussed yesterday, which I've stated explicitly twice now: the typography and plain sense of JS-in-itself suggests unary minus binds tighter than any binary connective (including **).

Saying "Math!" doesn't overcome this, any more than shouting "typography!" or "JS-in-itself!"

You don't get to pick a winner just because you root for one team. If you think the operator is worth adding and you concede that the other team's position is tenable because enough people are confused on the committee to predict a wider problem, then you have to consider whether the hardship of mandatory parens in the rare case of negated exponentiation expression is onerous.

It's not a source of runtime bugs, where test coverage is never good enough. It's a SyntaxError, so (a) Math uber alles people who (b) run into the rare hard case will learn to parenthesize.

I suspect that many Math-should-prevail types (I am among them in the abstract, prior to engaging with the human factors at play in JS) will want to parenthesize anyway, given the typographic appearance of precedence inversion. I will certainly do it if there's any unary prefixing an exponentiation expression in my code.

I did find a formula search engine. See www.searchonmath.com/result?equation=-+x^{y}&page=1&tm=1 for a taste of the hard cases (and false positives, which do not count).

Methinks you protest too much. Where is the common case made hard by proposal (4)?

# Claude Pache (7 years ago)

Le 24 sept. 2015 à 22:20, Brendan Eich <brendan.eich at gmail.com> a écrit :

Sez you! :-P

Seriously, the problem you are dismissing implicitly (bad form :-/) is the one we discussed yesterday, which I've stated explicitly twice now: the typography and plain sense of JS-in-itself suggests unary minus binds tighter than any binary connective (including **).

It’s more a question of convention than of typography, as in: x + y * z or: x || y && z.

There are two conflicting conventions here, not two conflicting typographies. For the set of operators defined in C, the convention of making every unary operator having a higher precedence, does not conflict with the conventions used in math. (It is not clear for me why that convention is important, just that it is a handy uniform rule that works well for some limited set of operators.)

Note that if you want to disallow -x**y, you might want to disallow x**y**z (which is imho more confusing), as there is no other right-associative binary operator (except assignment).

# Brendan Eich (7 years ago)

The worry over the meaning of (- x ** y) arises not just due to conventions or inference from typography, but from intuitions based on limited knowledge. As Dave Herman pointed out yesterday, many programmers (most) do not have the full precedence and associativity set memorized. When in doubt, they will rely on precedent in the language, visual cues including weight of operator (two chars vs. one), unary vs. binary. When in more doubt, they'll over-parenthesize.

The problem case is exactly the user cohort who'll not have enough doubt to over-parenthesize, who will rely on precedent and typographic weight clues, sound or not.

Mark Miller pointed out how many reviewers missed the problem with the original proposal as predicting likely confusion at scale. If we can avoid any confusion with an error, and force the people who should over-parenthesize to do so, while taxing the Math fans to do it when they must as well, this seems more prudent than just sticking with the Math-trumps-JS precedent and letting wrong results pass silently at runtime.

(I see no reason to disallow right-associative chaining. We haven't run head-on into confusion by drafting it the wrong way and getting it to stage 3 :-P.)

# Waldemar Horwat (7 years ago)

My preference is for 2, but I don't have objections to 4. Either works.

# Marius Gundersen (7 years ago)

One advantage of #4 is that it will make it possible to introduce either #2 or #3 in the future, if we change our minds. This way we can use real world use cases to decide if we should go for #2 or #3.

# Herby Vojčík (7 years ago)

Claude Pache wrote:

I just wonder why it is important that unary binds tighter? For instance, before I carefully studied the issue of this thread, I have never expected that unary minus binds tighter than binary multiplication operator in expressions like -2*x (although it does not matter in that case).

Making the parentheses mandatory here will be somewhat annoying in perfectly reasonable expressions, where you usually don’t use parentheses in real math notation., like:

let s2 = - x**2 - y**2 - z**2 + t**2

I would overcome it and do not write the parens:

let s2 = 0 - x**2 - y**2 - z**2 + t**2

Writing mandatory parens here is ugly.

In fact, I am surprised "-2" is unary minus with 2, I thought it is number -2. And similarly to Claude, I always read -xy in math notation, that is, as -(xy). Luckily, for multiplication it does not matter.

# Herby Vojčík (7 years ago)

An off-topic thought: Unary minus (and plus) are only used with numbers in JS. Why are they treated specially, not as hidden 0+x and 0-x, respectively? That would be logical (unary plus and minus would have same precendence as binary plus and minus).

# Brendan Eich (7 years ago)

Because C (B, BCPL, Algol). Too late to change JS where people do tricks such as !-x. No win in risking compat break.

# Kevin Reid (7 years ago)

On Thu, Sep 24, 2015 at 8:14 AM, Mark S. Miller <erights at google.com> wrote:

I like #4. Normally in a situation like this I would still argue for #1. #4 is a complicated special case that breaks the normal pattern of operator precedence elsewhere in the language. The need for ** is not great enough to justify introducing a new special case for users to learn.

However, in this case, #4 is only technically complicated -- for those writing or reading spec docs like us. For normal users, the only complexity is a rarely encountered surprising static error. With a decent (and easy to generate) error message, these users will immediately know what they need to do to repair their program.

Significant programs are read much more than they are written. Both #2 and #3 will lead many readers to misread programs. For programs that are not rejected, #4 is no more confusing than #1. Altogether, for readers, #4 is better than #1 because ** is more readable than Pow.

MarkM, I'm surprised you didn't also mention that there is precedent for #4 from your own E.

The E language chose to place math operations as methods on numbers, rather than on any static "Math" object, and does not have an exponentiation operator. In order to avoid precedence surprises of the category we're discussing, E statically rejects the combination of a unary prefix (negation) and unary postfix (method call) operator.

-(2).pow(2)   # "ought to be" -4, is a syntax error
-(2).max(2)   # "ought to be" 2, is a syntax error

(The parentheses around the number are not actually required in E, but I have included them for the sake of comparison to JS despite the lexical rejection of "1.foo" in JS.)

JavaScript already syntactically accepts the above programs (parsing "-" as lower precedence than ".foo()"), but #4 is in the same spirit of rejecting cases where there are conflicting or unclear precedents for operator precedence.