Designing a programming language, I’m including the **
exponentiation operator. In Fortran and Python, the two languages I know of which have this operator, it binds more tightly than unary minus, which makes sense for practicality as well as tradition.
It should bind more loosely than the prefix increment operator ++
because the other way wouldn’t make sense. Thus the usual single precedence level for the prefix unary operators gets split in two, which is fine as far as it goes.
I’m also including the logical and bitwise not operators !
and ~
. Which side of the split should they fall on? Should they bind more loosely or tightly than **
? I haven’t been able to find either objective reason or relevant precedent for either choice. Is there any of either that I’m missing? Failing that, which would people find less surprising?
6
When designing a language, you can tweak precedence and other rules to create an elegant experience. Sometimes, not allowing certain constructs can improve readability, without sacrificing much expressiveness. You only loose similarity to other languages, thus making your language harder to learn.
You established -x**y == -(x**y)
. This makes sense, as this is the convention in maths.
You are considering ++x**y == (++x)**y
. The alternative does not make sense, because x**y
is not an lvalue. However, you may want to consider disallowing increment on an expression level. E.g. Go takes this route. This avoids the problem that x = ++x
is undefined behaviour in many languages. Also, x++**y
is downright ugly 😉
If you have a static type system and a boolean type, then specifying precedence between **
and !
does not make sense: One is a numeric operator, the other is logical.
If you have no boolean type, or no static type system, then !
should have lower precedence, so !x**y
is !(x**y)
. Exponentiation should bind very tightly, only increment and method calls should have higher precedence. Also, the interpretation (!x)**y
is wonky: Few would expect that, and you should generally follow the principle of least surprise. This would also give !
a higher precedence than unary minus…
I personally am a fan of alphabetic logical operators like not
, and
, or
with very low precedence. This can improve readability.
I am not a fan of bitwise operators like <<
, ~
, &
because bit twiddling is only useful in very few domains and these characters would be valuable for other operators. <<
could be some stream operator, ~
could be string concatenation. Look into Haskell or Perl6 for cute ideas.
If you want to include bitwise operators, then they should relate in precedence to their logical equivalents: !
and ~
might have same precedence. ^
(xor) and |
should have roughly same precedence, and bind slightly more tightly than the logical operators ||
and &&
. &
should have higher precedence than |
, just like &&
usually has higher precedence than ||
.
To avoid suprises, stay near C, as it is one of the most influential languages. You can find a precedence table on Wikipedia. Compare with Perl, Python. Also remember that some languages in the ML family survive without such elaborate precedence tables.
7
I don’t have anything to back it up, but my feeling tells me that the logical and bitwise negation operators (!
and ~
) are most closely related to the unary minus (-
) and should have the same precedence level.