This actually gets weirder - in python you can make an arbitrary long comparison operator - it's called comparison chaining - https://www.programiz.com/online-compiler/6uyqb52IVH8if . It works with a lot of operators - https://www.geeksforgeeks.org/chaining-comparison-operators-...
Once you know how it works and are used to it, I think it makes the code easier to parse... but there are heavy downsides. For example, it's not clear how short circuiting works. I've used python a bunch and logically I expect ```side_effect3()``` to not be evaluated if results of 1 and 2 are not equal: ```side_effect1() == side_effect2() == side_effect3()```. However, I do not know that for sure, while in other languages I would be able to reason about this from basic principles.
I think most people would expect expressions like `5 < x < 10` to work as they do in math, without necessarily thinking about it in terms of being an overloaded binary operator. The result in other languages that `5 < 12 < 10` = `true < 10` = `true` is more surprising, just that we've (perhaps by being bitten by it early on) gotten used to it.
It's not a hard rule that overrides all other considerations, but I do think Python's choice to follow math is the right decision here.
I'd claim if you teach someone `<` then show them `5 < x < 10` for the first time, they're far more likely to go with the math interpretation than the C one. That is, beyond the fact that someone's familiarity with math reinforces this intepretation, the reason for math using this interpretation in the first place is because it aligns with intuition.
It's also just pretty useful. It's common to want to check if a number is between some bounds, like `0 <= i < len`, or that multiple things are equal. In cases where you really do want the C behavior, I'd say you should already write that as `(x == y) == z` for clarity anyway, regardless of whether the language lacks chaining and thus lets you omit the parentheses.
x = x + 1 sounds wrong if you are in math mode
x := x + 1 is uncommon in math and you can take it to mean "set x to whatever is currently x, plus one"
But it's also true that you need to be able to accept certain conventions when changing fields.
It is clear if you read the docs.
https://docs.python.org/3/reference/expressions.html#compari...
https://docs.python.org/3/reference/expressions.html#boolean...
I once passed an interview where it was all just Spring documentation questions. I had never built a Spring app in my life, I just "read the docs" five times.
I’d expect a raised eyebrow if it was some very basic question about something you’ve claimed to be an expert in but given how many, many bugs over the years stem back to confusion about order of evaluation I would consider it a minor plus if someone said “I think I know but I want be certain”, and a major one if they paired that with mention of defensive coding practices like adding tests covering cases where it’d matter and structuring the code so there are no side effects hidden in the test (e.g. the code as written is fine but if it was f1() == f2() == f3() I’d probably write it as a clearer multi-line if block when there are side effects to those calls to make obvious when I’m intentionally not doing them in every case).
Although I'd also want that benefit of the doubt for myself. I'm quite familiar with how Ruby handles collections, for example, but if I tried to use `slices.Collect` in Go the same way as `Enumerable#collect` in Ruby I'd end up stuck and would need clarification.
Extra points if you are familiar enough with the documentation to find the answer quickly.
Example
(a==b)==c would be different from a==b==c
Opinion: Code that depends on side effects like that in a conditional is BAD. It is not an optimization, it's an obfuscation.
There's a lot of options for a language to deal with a statement like this. It could be a syntax error, a compile or lint warning, it could work by doing order of operations and comparing the boolean from one compare with the third element, or it could work in the way you described.
I'd prefer languages that I work with to chose earlier options from that list. In most languages this sort of statement is usually a bug, and deserves explicit parenthesis to clarify the order of operations. I really don't want to have to pull out an operator precedence chart in order to read your code, much less have to look up some esoterica about this specific language.
Each rule of a language is a cognitive burden. It can only justify itself if the thing it replaces is a greater burden.
I've worked primarily in Python for the last two years and I'd still have to look at a reference to be certain of what exactly this code does. And would probably rewrite it to the expanded, parenthesized form unless the company linter insisted.
Hard disagree here. I write code for people who can read the language. This includes operator precedence.
Programming languages often have syntax that's acknowledged as a source of confusion and bugs and better avoided. The line is not hard and fast, but I write C++ and there's "C++", and then there's "the C++ subset that you should use" (I probably couldn't write C++ code without tools slapping me for using the wrong parts). Operator precedence is debatable but our tools force the use of parentheses to disambiguate.
People unfamiliar with programming will assume that "if a == b == c" is only true if all three things are the same, and people familiar with Python will know that that is, indeed, what it means.
Additionally, I think assuming the reader is aware of every. nuance of the language is an extremely bad idea unless you are and will always be the only developer. If you are doing anything weird or tricky or rare, I'd highly recommend linking the documentation or providing an explanation.
Python 'won' mostly because non-programmers could look at it, more or less understand what was going on, and feel that this was something they probably could learn.
"There are no children here at the 4H club either! Am I so out of touch?
No... it's the children who are wrong."
-Principal Skinner, "The Simpsons"
EDIT to clarify- I'm just being silly, not suggesting anyone is right or wrong here.
But then is the mathematical '=' really a binary operator that maps two numerical inputs to a boolean output? It feels then as an abuse of notation if (A=B)=C doesn't allow A=B to change type?
Because I don't really have much use for a symbol that is some times doing a binary mapping from numbers to booleans and some times becomes some sort of ternary operator which maps 3 inputs to one boolean.
5==5==5 # All three the same
(5==5)==5
true==5
false
Or 1==2==false # All three different, in a troublesome way
(1==2)==false
false==false
trueCompare this with
a = b = c = 5
You evaluate the innermost (i.e. the tail) expression first, and work your way out to the head. As a sexpr it is a little more obvious that way: (= a (= b (= c 5)))
An argument could easily be made that python is making up special rules for the equality operator by treating the duplication of the operator as merging the expressions into one.Instead of what you would expect, per the rules of literally every other expression, 5 == 5 == 5:
(== 5 (== 5 5))
It gets rewritten as (== 5 5 5)
Which one is "unexpected" is really a matter of perspective: are you reading with the rules of English or the rules of your programming language as your context?I do concede one caveat to this argument: mathematical operators tend to change operator precedence around to avoid making mathematical mistakes. I am mildly of the opinion that this itself was a mistake. Most calculators don't do it, so it's not like anyone would be* that* thrown off by it.
I guess the argument could be made but it would be wrong. All the comparison operators can be chained in python. a < b < c, for example, or even a == b <= c < d.
That's not true in Python though.
a = b = c = <expression>
is equivalent to: temp = <expression>; a = temp; b = temp; c = temp
I don't know why that order of evaluation was chosen.Sure, that's true if you literally know nothing about coding. But that is not a very common audience for reading code. You only need to spend about 10 minutes coding to realise that the compiler follows fixed rules rather than making an LLM-like guess as to the meaning. If you get that far then most people (admittedly after a bit more 10 minutes) go on to realise that the only way to know what code does is carefully pick it apart step by step, rather than glance at it and guess yourself.
I love Python dearly, but this rule was a misstep on my opinion.
1 + 1 + 1 has different operator behavior then 1 == 1 == 1. The operations here are not consistent and it’s not clear what happens if I overload the operators.
On the surface level python looks more beautiful. But mathematically speaking python is actually more ugly and more inconsistent with weird rules to make things look a certain way.
(= 2
(+ 1 1)
(sqrt 4))
; => t
(< most-negative-fixnum 0 most-positive-fixnum)
; => t 2 + 3 + 4
you write (+ 2 3 4)
This is nice for associative operations like + or *, very very very slightly confusing for - and /---
You use the same style for comparisons, so instead of
2 == 3 == 4
you write (== 2 3 4)
To support the fist "infix" syntax, you need some magic in the == operator. But the second "prefix" syntax is totally natural and you need no magic in the parser or the operator.It's fine if you DNF it:
(- a b c) => (+ a (- b) (- c))
(/ a b c) => (* a (/ 1 b) (/ 1 c))The functions behave like:
= all the same
/= all different
< monotonically increasing
> monotonically decreasing
<= monotonically nondecreasing
>= monotonically nonincreasing
Also * is product and + is sum.