5 ms·
Funnily enough, APL is as unambigous as possible when it comes to order of execution. It's _always_ right-to-left So ∇⍵-2 does ⍵-2 the applies ∇ to it. Simmil
by maest 7y ago
Funnily enough, APL is as unambigous as possible when it comes to order of execution. It's _always_ right-to-left
So ∇⍵-2 does ⍵-2 the applies ∇ to it.
Simmilarly (and initially confusing), 2+45-2 evaluates to:
(2+(4(5-2))) = 14
From experience, the simplicity of not having operator precendence is liberating (why do we even need operator precendence in the first place, really?).
- avmich 7y agoI'm sure in the numeric example the forum engine made the result wrong. Let me try. 2 + 4 * 5 - 2 should be 14...
- kazinator 7y agoIn HN, asterisks denote italics, which is troublesome for citing pieces of code in-line. Look at your rendered comment! Reliable code formatting is achieved by putting code in its own paragraph that has at least two leading spaces on each line.
- maest 7y agoOops, you're right. Unfortunately, it's too late to edit the post now (can't find an edit button).
- kazinator 7y agoWhy we need operator precedence is so that math notation can properly serve as a "tool of thought". The correspondence between arithmetic laws and an algebraic notation which remains sane when not fully parenthesized requires precedence levels. Higher "power" operations must be placed at higher precedence levels. For instance, given A(x + y + z) we can distribute A into the trinomial factor: Ax + Ay + Az That doesn't work if that result has some wacky interpretation like: A(x + A(y + (Az))) And under this rule, although you don't have precedence, you still have an implicit rule of associativity: multiplication and addition share the same precedence level, and associate right to left. You have not been been liberated from ambiguity that requires invisible rules. Why the higher-powered multiplication must have precedence over lower-powered addition is that at the most rudimentary level, it represents repeated addition. 3y means y + y + y arithmetically. If we want that equivalence to be mirrored in notation, we need the precedence. This allows us to take an expression like x + y + y + y + z and confidently rewrite y + y + y as 3y. The properties of addition and multiplication assure us that it's mathematically correct, and the properties of the syntax, assure us that the symbolic manipulation is semantics-preserving: replacing multiple terms of a sum by a single product has no interaction with adjacent terms due to precedence. That's the gist of it: correspondence between syntax rewrite rules and the semantics of algebraic rules.
- maest 7y agoI'd argue that having A*x + A*y + A*z evaluate to A*(x+(A*(y+(A*Z)))) seems wacky because we've been instilled the idea of operator precedence from early school years. So now, when you see A*x + A*y + A*z you instantly do the grouping in your head. That's all convention really. (And, sure, conventions are powerful). The point i was making about simplicity was at a mechanical level. The rule "everything is evaluated right-to-left" is mechanically much easier than having, say, 15 different levels of operator precedence[0]. Another point to make here is that + and * are only 2 operators in a much larger list of operators present in a programming language. So even if you have a strong bias towards following the convention and having * have precedence before +, you still need to figure out evaluation order when new operators are included. There's a spectrum between spartan simplicity and baroque APIs with lots of convenience features and I find that having a sparse set of simple rules is less mentally taxing and more effective than big API surfaces. [0] https://en.cppreference.com/w/c/language/operator_precedence https://en.cppreference.com/w/c/language/operator_precedence
- kazinator 7y agoThe reason we group A*x + A*y + A*z in our heads somehow is that it has a repeating pattern. Either the grouping is (A*x) + (A*y) + (A*z) Or else it must be A*(x + A)*(y + A)*z. It's not obvious that the A*(x+(A*(y+(A*z)))) interpretation of A*x + A*y + A*z lends itself to manipulations of syntactic units that corresponds to arithmetic in the same way. We can't factor out A. If we divide by A, it just goes to x + A*y + A*z. Then if we divide by A again, we get (x/A) + y + A*z. We don't have the concept of commutative laws being reflected in the ability to simply exchange syntactic elements that are siblings at the same level in the tree. For instance, these are commutative pairs: A*x + A*y + A*z <==> A*x + A*(A*z) + y. We can't lose the parentheses around (A* z) because the interpretation of the syntax doesn't respect the boundaries required to express commutativity. The proper way to eliminate nuisance N-level is Lisp syntax, not some silly version of associativity without precedence. Then we still have those algebraic properties: (+ (* A x) (* A y) (* A z)) --> exchange nodes to commute --> (+ (* A z) (* A y) (A x)) --> factor A: (* A (+ x y z)). It can get cumbersome, but we have a sane way to do algebra by manipulating sanely delimited subtrees in regular ways. People have developed (more importantly, debugged) computer algebra systems in this representation.