Exponentiation operator precedence
Brendan Eich
brendan at mozilla.org
Thu Sep 24 14:11:35 UTC 2015
Quick update from TC39 yesterday where Rick and I presented the Stage 3
Exponentiation Operator proposal:
http://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 -(x**y).
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.
/be
Jason Orendorff wrote:
> 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".
>
> -j
> _______________________________________________
> es-discuss mailing list
> es-discuss at mozilla.org
> https://mail.mozilla.org/listinfo/es-discuss
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20150924/fe3425a8/attachment-0001.html>
More information about the es-discuss
mailing list