The Python language has a neat feature: An expression like x < y <= z is interpreted, according to mathematical convention, as equivalent to x < y and y <= z. Operands are evaluated only as many times as they appear, and the short-circuiting nature of the equivalent operation is maintained. For example,
if f() <= g() < h() <= i(): whatever
behaves as if the code were
x1 = f()
x2 = g()
if x1 <= x2:
x3 = h()
if x2 < x3:
x4 = i()
if x3 <= x4:
whatever
(Thus h() and i() aren't evaluated unless necessary.)
As far as I am aware, the first language to support this was CPL, as demonstrated on page 6.1.3 of the CPL working papers. However, CPL was never implemented fully, so it could be argued that the feature was never supported, per se. Were there earlier examples?
a < b < cmeans something entirely different from(a < b) < cora < (b < c)will be more complicated—harder to write, harder to understand—than the parsers used in other programming languages. And, if it's harder for a program to parse the expression, then it's harder for a human to understand it too. Of course, once you've learned what it means, then it's easy enough to see, but before you could "see" it you had to learn one extra thing about Python expressions than you didn't learn for other languages. – Solomon Slow Jun 23 '21 at 14:07True. The expressiona<binherits its left value froma, inherits its right value fromb, and it's true if; the right value ofais less than the left value ofb, AND the truth value ofaisTrue, AND the truth value ofbisTrue. – Solomon Slow Jun 23 '21 at 17:10a < b == c <= dnot only impliesa < b,b == c, andc <= d, but if comparisons are transitive, it would also imply thata < c,b <= d,a < d, etc. whether or not code explicitly tests those conditions. As an alternative to chaining, I wonder if languages could benefit from "push" and "pop"operators, so if $ is push and @ is pop, @ would yield whatever value was pushed by the previous unmatched $ in the expression, in source-code order. – supercat Jun 23 '21 at 17:24<such that(< a b c ...)would be true if and only ifa<bANDb<cAND... – Solomon Slow Jun 23 '21 at 17:28unsigned short temp = *p; return temp - (temp >> 15);intoreturn *p - (*p >> 15);, which loads the value twice and may behave oddly if the two values differ. – supercat Jun 23 '21 at 17:34less(a, b, c)in more conventional notations. Maybe you don't notice in LISP since everything is prefix, and elsewhere the distinction between infix and prefix is obvious? – dave Jun 23 '21 at 17:42*pwhile another thread could be modifying it in the first place. Otherwise yes, even in asm without invented reads you can get tearing, and even store-fwd a value that was never globally visible for another core to have loaded: Globally Invisible load instructions – Peter Cordes Jun 23 '21 at 17:53<,=,>,<=,>=, and/=work in Common Lisp (along withchar<,char=,char>,char<=,char>=,char/=,char-lessp,char-equal,char-greaterp,char-not-greaterp,char-not-lessp, andchar-not-equal). – texdr.aft Jun 23 '21 at 20:21f() < g() > h() <= i(). – dan04 Jun 23 '21 at 21:09a < b < c, but things go down hill when you allowa > b < c, and further down hill when you start includingin/is(and their negated counterparts) in the chaining. I don't think anyone ever wanted to writea < b is c. – chepner Jun 24 '21 at 15:51<operator when called with 3<x when x is 2 can produce2 but false. The parser does have to decide which argument to propagate based on context, though. – JDługosz Jun 25 '21 at 13:50