I think implementing operator precedence in ANY programming language is a mistake. `1 + 1 * 2` should be a syntax error. In C, in Python, in Java.
I think implementing operator precedence in ANY programming language is a mistake. `1 + 1 * 2` should be a syntax error. In C, in Python, in Java.
A total ordering has a definite answer to the question "is x greater than, equal to, or less than y?" for all x and y. A partial ordering will answer "I don't know" for some x and y.
It's quite possible to implement operator precedence using a partial ordering. When the parser has to resolve precedence between two operators and their relationship is not defined, throw an "ambiguous precedence" error.
You can implement the partial ordering by putting all the operators into a DAG of sets of operators. If two operators are in the same set, they have equal precedence. If there is a path through the DAG from one operator to the other, that defines the precedence relation. Else there is no relation.
Say "*" is defined to have a higher precedence than "+", and they both have an undefined precedence relation with "&". Then "1 + 2 * 3" should compile into "1 + (2*3)", but "1 + 2 & 3" should throw an "ambiguous precedence" syntax error.
For the same reason that math has defined precedence (and even omits multiplication signs entirely in many places), programming languages in some domains will choose to do the same.
It’s not a problem if you want to create a language that does not do this, but I think it is a problem if you want to ban others from doing it.
Even multiplication is often distinguished more by the shape of the text than by actual precedence rules, once you leave basic arithmetic - expressions like "1 - 3xy + 2y²" make the order of operations obvious from text layout, which is why they are preferred over "1 - 3 × x × y + 2 × y ^ 2", which no mathematician ever writes.
Similarly, in the following expression:
x
_ + 2
2
I don't think it's precedence that makes it clear you first divide by 2 and then add 2 to the result. y = x / 3 + 2
y = 2 + x / 3
y = x/3 + 2
We all agree those are the same, despite a lack of an any cues in the first two. That suggests to me that there is a defined precedence for division vs addition in math. (So much so that it doesn't even seem like a controversial statement to make.)People are generally bad at remembering arbitrary operator precedence rules. That is why most math notation doesn't rely on precedence rules to be unambiguous, it uses other kinds of textual clues. For example, the / operator is virtually never used in algebra. Instead, division is indicated by fractions, which explicitly group their operands and separate them from other adjacent operations. Exponentiation relies on super scripts to separate its arguments, making it extremely unambiguous at least which operations are part of the exponent and which the total. Roots use the radical symbol which extends over the entire expression you're taking the root of, and again rely on a kind of superscript for indicating which root you are taking. Limits and sums use subscripts to separate the iteration direction from the expression. Integrals use both sub and superscripts.
The textual representation even for exponents is not enough if you don't already know that exponentiation has higher precedence than multiplication.
"1 - 3xy + 2xy²"
Both of us know that only the y term is squared in that expression. How do we know that?
I claim that we know that because we know the precedence rules and that someone never exposed to our system of algebra would not inherently and unambiguously know that it was 2·x·(y²) rather than (2·x·y)² or even 2·(x·y)² merely by visual inspection of the expression.
This (the meaning of the above expression, including the precedence/grouping) is something that has to be taught in Alegbra/Prealgebra courses, and is something that some students struggle with.
1+3
x + 4
In this case, the exponent notation makes it unambiguous that this is (x^(1+3))+4, and not x^(1+3+4).I agree that xy² could mean both (xy)² and x(y²), and that perhaps you need to understand precedence to disbiguate. I would still contend that the notation is defined as "each term has its own exponent, possibly 1 which need not be written" to disambiguate, where terms are separated by parentheses or by other operators.
That is, in my mind, the grammar for usual algebraic notation is something like this:
expression = term (op term)*
expression___________ (expression)?
term = (num | var | fraction | \( expression \) | \/expression)
op = + | - | / | × | ° etc
And the rules are that you first evaluate each term, and then the operators between the terms. You only need to follow precedence rules for those operators between terms, the evaluation of a term is unambiguous.Precedence, under the term "order of operations", predates computers by several centuries. Which is not to apply that it was fixed in stone from the beginning, or perfectly consistent, but mathematics agreed on the modern rules universally in the first couple decades of the 20th century.
This is nonsense. There are no underlying mathematical precedence rules. You're seeing precedence being represented visually, but there is no mathematical difference between 5 3 ADD 2 MUL and 5 3 2 MUL ADD.
MUL and ADD are just binary operators, or if you prefer, functions from U × U to U for some set U.
Your statement is particularly nonsensical as applied to the example you're supposedly responding to. Given some expressions:
5 + 3 5 5
------- ; --- + 3 ; -------
2 2 2 + 3
How do you even form the argument that the difference in interpretation is "simply the graphical interpretation of the underlying precedence rules"? The precedence rules here are obvious, because they don't exist:- Division occurs between the upper operand and the lower operand
- Addition occurs between the left operand and the right operand
It's impossible for these to conflict, so there is no such concept as precedence.
(Precedence also doesn't exist between addition and addition because addition is associative. It is necessary between division and division, and the system is straightforward: lower precedence is indicated by longer lines. Tell me about the underlying mathematical precedence rule that determines that.)
You might want to correct the information here then:
https://en.wikipedia.org/wiki/Order_of_operations#Convention...
> These rules are meaningful only when the usual notation (called infix notation) is used. When functional or Polish notation are used for all operations, the order of operations results from the notation itself.
You could "correct" that to observe that "the order of operations results from the notation itself" is true of all notation, including infix notation, but why? It's not wrong now.
Can you really not tell the difference between "notation" and "math"?
They aren't a fact about mathematics. They're a fact about notation. Operator precedence and order of operations are synonymous.
Math has very well-defined, well-trod conventions that are densely packed into the notation. When people try to write code like this though, it’s hard to read.
All else equal, I’ll take a snippet of code with more characters in it but less work to unpack those characters. Reading linearly is cheap; I get tired reading formulas faster than reading technical prose. Precedence makes the mental parsing nonlinear.
(a + b) / 2π
instead of
(a + b) / (2π)
It seems that math teachers put division above multiplication in order of operations but programming languages almost universally treat division and multiplication as equal and read them left-to-right. I've fixed multiple bugs before that were due to this.
For example the APL syntax for expressions, which I prefer, is that there is no operator precedence.
The operands are associated to operators strictly from the right to the left, regardless which operators or functions are used, which is a much wiser choice than the reverse traditional order of evaluation.
No parentheses are used, except when they are needed to modify the default evaluation order.
a | b || c >> d & e * f
? I think its reasonable to say that a language should not allow thatBut all the precedence rules you're invoking here are useful in isolation, and I see no reason why they shouldn't parse in combination. A linter can use heuristics to flag cases which should be parenthesized, a parser would need a defensible rule for failing a parse. What would that rule be?
If you can do it, someone will do it (in prod, in your product).
Not in expressions like 1 + 3 * 6. Personally, I include the parentheses anyway, as a matter of style, and would write 1 + (3 * 6).
But in expressions like i + 3 > 4 / n? No thank you, (i + 3) > (4 / n) would drive me up a wall. Ymmv.
in c, x == 1 || y == 3 works fine, but unfortunately the bitwise operators have the wrong precedence; x & 2 == 2 is parsed as x & (2 == 2), which is never what you meant. so even the best software designers have made terrible blunders in precedence design
math formulas are a bigger issue; 3*x**2 + x/2 + 1 is a lot better than the fully parenthesized version
However, there are also operations with crystal clear precedence relationships, such as logical conjunction and comparison, and in that case it has to be asked what exactly would the parentheses be there for?
In lisp the answer is: to enable syntactic homogeneity and to enable macros.
But in other languages like Python that don’t have sexpr based syntax, parenthesizing everything would just be pointlessly unreadable.
https://hyperscript.org doesn’t allow mixing different infix operators w/o parentheses