Better Operator Precedence
scattered-thoughts.net
scattered-thoughts.net
One of nice things about such a system is its compositionality: If you mix and match custom operators from different modules, you wouldn't get a conflict or a random precedence order, instead, you'd simply be forced to disambiguate using parentheses.
Contrast this with Haskell, which also has custom operators, but only a fixed number of precedence slots (10 IIRC), and some libraries really suffer from the fact that there's no "good one" available that works for the intended usage.
https://github.com/apple/swift/blob/3ea9e9e55281b9957d2b5486...
(My understanding of the article is that it mainly argues for _partial_ precedence, not so much that precedence/associativity can be defined in the program - the latter is also the case in other languages, e.g., Haskell.)
precedencegroup MyPrecedence {}
infix operator +++: MyPrecedence
func +++ (lhs: Int, rhs: Int) -> Int { lhs + rhs }
let a = 1 + 2 +++ 3
Error: Adjacent operators are in unordered precedence groups 'AdditionPrecedence' and 'MyPrecedence'Always code for someone to read your code later.
if (((2*x) + 3) == z)
instead of if (2*x + 3 == z)
If so, that’s pretty annoying.But another tool to work around this issue is to just give names to sub expressions by storing them into constants. Long expressions quickly get unreadable anyway.
[1] https://en.cppreference.com/w/cpp/language/operator_preceden...
But if it's really so onerous to read, you can always put a more ambiguous version in the comments!
As the saying goes, "the fish rots from the head"...
RPN might be a better starting point for a revolution.
Perhaps the extra tree parsing required by the brain is more than made up for by some kind of visual compression economy going through the eyes?
sexprs without pair matching are horrid, but it's a solved problem since a few decades (paredit was made by zeus)
For example, the proof of the Minkowski inequality in Wikipedia [1] contains a statement of the form "x_1 = x_2 = x_3 ≤ x_4 = x_5 ≤ x_6 = x_7". Except that x_1, ..., x_7 are so complicated that any notation that requires repeating them doesn't feel like a satisfactory notation for "all of mathematics".
I find infix operators and their standard mathematical precedence rules perfectly intuitive. Equally good are the extended precedence rules of languages like R, which were designed by professional users of mathematics.
I hate reading S-expressions and reverse polish notation. To me, proposals that we write in those notations are like saying that we should write assembly code. I think "No, we have compilers so that humans can write human-readable code rather than being forced to cater to the machine."
Does "<<" in C have the same precedence as "shl" in Pascal? How does it compare to multiplication, of which it is (essentially) a specialization? Does a<b<c in C mean what it does in Math? What about Python?
When I learned APL (and J and K), the first instinctive response to the lack of operator precedence was wtf? -- all same precedence, all "right associative" / "right to left" / "left of right" / "long right scope" (same meaning, different terms).
But after using it for a day, I realized all the other programming languages have it wrong. Math notation gets a pass because you handwrite it, so a set of rules that minimizes writing does make sense. Not so for programming languages.
APL/J/K have tens, perhaps even a hundred, operators -- so there isn't really any other practical way. But it just works so well, that it puts the Algol/C/Pascal decision to copy math in an unfavorable light.
*: x " x squared
y *: x " NAND of x and y
At least it made it to the Problems with BQN page ;)https://mlochbaum.github.io/BQN/commentary/problems.html#inc...
Write it out clearly and use extra () clarity to be completely obvious what is meant.
What's the problem? What is the point of this article?
If I have to stop and think about operator precedence, that's a bad sign.
It's not obvious to most people what precedence to expect
Nothing is obvious to anyone who hasn't learned it yet. Where do you draw the line? The rules of a natural language are orders of magnitude more complex, yet humans have no problem learning and using them. There are twenty-six letters in the English alphabet, and "most people" wouldn't have trouble answering if C comes before or after J. Yet you look at the few more levels of precedence in this other language you use nearly every day, and "OMG too hard!!!!11" ?
It's sad to see this strange sentiment of "anti-intellectualism" or "anti-learning" that seems to be slowly growing in the programming community; and turning a linear one-dimensional ranking into a two-dimensional sparse matrix of comparisons seems like the exact opposite of a solution or reduction in complexity.
If I invent a crazy unit system for measuring distances, where a "finger" is 8.3 centimeters and a "storey" is 246 centimetres and start using it in my writing, can I really blame others for "anti-intellectualism" when they refuse to learn my arbitrary new unit system?
The problem with operator precedence is just that - any operator precedence rule is an arbitrary convention. I'm not saying that conventions are never worth learning, but it depends on (1) whether such convention is already well understood, and (2) whether the benefit of such conventions outweigh the trouble of requiring everyone to learn it. The precedence between * and + clears both criteria: most people have learned during school, and it's quite common to use both * and + in the same expression that defining that * binds tighter than + can save a lot of parentheses. On the other hand, combination of + and | are rare enough that the benefit of such conventions doesn't outweigh the confusion it could cause.
On the other hand, combination of + and | are rare enough that the benefit of such conventions doesn't outweigh the confusion it could cause.
Maybe for you it is, but for others it isn't.
I suspect a lot of this "refusal to learn" sentiment is actually driven by management attempting to turn developers into easily replaceable, expendable "resources". From that perspective, lower-common-denominator dumbing-down barely-literate workers is their goal, and so we should definitely strive to fight against that and the degradation of our craft.
I think it actually gives a nuanced answer to your question "where do you draw the line", namely: With a partial precedence scheme, one does not need explicit parentheses for those operator combinations for which the precedence is "clear enough". E.g., in "3 + 2 * 4" precedence is clear to virtually all programmers because it follows standard rules from math ("PEMDAS"), so that is why the precedence table in the post specifies an ordering. However, especially for operators which are less frequently used, or where precedence does differ between languages, one should give parentheses for clarity. I think this is a very sensible argument.
I have certainly made errors because of unclear precedence (e.g., with boolean operators, exponentiation, casting etc.) in the past. And given that there even is a CWE number (https://cwe.mitre.org/data/definitions/783.html) for this kind of error, it seems frequent enough to warrant discussion.
Maybe we need a song to teach operator precedence. The PEMDAS acronym doesn't have the same lasting impact as the alphabet song it seems. Does Sesame Street feature any segments on arithmetic? I don't remember any from when I was a kid. Are kids who watch Sesame Street too young for that?
To be fair, this has a lot to do with the fact that C is pretty early in the alphabet, where it's easy to enumerate until you hit C before J. A lot of people couldn't say off the top of their head whether S comes before or after O, or whether R comes before or after V.
Making languages users remember whether + has higher precedence than | is 'unworthy' complexity, the only reason we have such things is that it simplified language implementation..
Basically, an expression "A + B" is given a certain precedence, and both A and B are expected to have higher precedence. You can modify that by putting a ` in front of any nonterminal, for example "`A + B", which means that A can have indeed the same precedence as "A + B". This way you can specify associativity, and this works also in other cases, for example "∀ x. `P" means that something like "∀ x. ∀ y. x = y" parses correctly instead of having to write "∀ x. (∀ y. x = y)"
Also, they should mention ternary operators.
However, given the low popularity of Bison, and that it's mainly used for its old features, I'm not sure that has ever been used in the wild...
I.e., with no parenthesis it works exactly as a layperson typing numbers into a calculator and feeding the answer into the next calculation. And with parentheses, the layperson can specify some calcs to happen out of order.
d = a + b * c
I expect it to be evaluated as: d = a + (b * c)
Maybe that’s just me, but I bet I’m not the only one that thinks that way.For non math operators, you may have a point…
I’d love to imagine a little number monster pacmanning his way across the line, munching numbers and operators that change his colour and effect.
https://pharo.org/documentation
site:pharo.org smalltalk