
Many programming languages have infix expressions, but get associativity wrong - blasdel
http://jfm3-repl.blogspot.com/2010/01/associativity-and-muse.html
======
miloshh
Rather than a number of languages getting associativity wrong, a simpler
explanation is that the < operator is not associative.

Mathematically, studying associativity makes sense for mappings of type A x A
-> A, where A is any set; < is not such a mapping.

~~~
ramchip
But... isn't that what the author says?

 _Haskell is right. Sugaring in the conjunction like Python violates non-
associativity, which exists for predicate relations in human languages. And
it's not a type error; it's a syntax error.

Note that in mathematics one sees things that look like "1 < i < n", which
seems similar to what Python allows. But, if you ask a mathematician to read
this aloud, they will say "some i between one and n", or in certain contexts
"all i between one and n". This is an entirely different thing; it is not a
predicate at all._

[...]

 _So with <, it's non-associative and bad to sugar up the error you get if you
try to use it associatively._

~~~
miloshh
Hmmm... but by the same argument, Ruby is also doing it right, by correctly
crashing at runtime (there is no way for a language without a type-system and
with redefinable operators to discover the error at compile time).

Somehow the author isn't clear on whether he wants "1 < 2 < 3" to return true
or to be rejected; if it's the latter, then most languages get it right
(except Python and Matlab, as far as I know).

~~~
ramchip
I think he's actually quite clear on rejecting it as a _syntax error_. Ruby
isn't doing it right in that it throws a _type error_ : 1 < 2 < 3 becomes
lt(lt(1, 2), 3), then lt(true, 3) which doesn't check.

I know nothing about Ruby so I don't know how its operator overloading works,
but I think that could be a problem if < was redefined with an a -> a -> a
signature, or a -> a -> b with b comparable to a, since in this case you
wouldn't get the type error. The proper solution, according to the author,
would probably be to simply refuse any expression with more than one < (or any
other non-associative operator).

~~~
miloshh
It doesn't seem possible to consistently make "1 < 2 < 3" a syntax error in a
language powerful enough to redefine <. (Including Haskell, where you could
easily hide the Prelude definition and define your own, also changing the
fixity declaration.)

So I guess a fair compromise would be if the default implementation rejects "1
< 2 < 3" at either compile-time or run-time, whichever is possible.

------
biotech
The fundamental misunderstanding here is how we relate our parsing of
mathematical expressions to the way a compiler parses expressions:

While we can parse the following expression properly:

    
    
        a < b < c
    

parses to:

    
    
        (a < b) AND (b < c)
    

obviously most compilers don't do that. As others have pointed out, the <
operator should not be associative.

The misunderstanding, though, is that the original expression

    
    
        a < b < c
    

actually could be parsed (I'm making a bunch of assumptions about the grammar
and parser type here) by treating ..<..<.. as a _ternary_ operator, like ?: in
C++.

Then, in the syntax tree, the compiler could re-organize the expression to be
the correct an unambiguous:

    
    
        (a < b) AND (b < c)

------
anamax
If associativity and precedence are "natural", shouldn't there be near-
universal agreement?

I mention that because the standard argument against lisp notation is ....

------
johnbender
"Ruby, in an adorable little attempt to wear big-boy pants, is turning this
expression into the following pseudo-code"

Is the condescending tone necessary?

An alternative:

"Ruby turns this expression into the following pseudo-code"

~~~
jamesbritt
The author, in an adorable little attempt to wear big-boy pants[0], seems not
to know that '<' is a message passed to a receiver. You can even redefine it
if you like.

Better pseudo code would be

    
    
        1.lt(2).lt(3)
    

In fact, this is correct (albeit ungainly) ruby code

    
    
        1.<(2)  # true
    

and so

    
    
        1.<(2).<(3) # Say wha ... ?
    

makes no sense.

[0]: Yes, a cheap shot.

~~~
mikedouglas
The underlying mechanism doesn't matter if it's wrong.

Those familiar with the mathematical notation would think:

    
    
         1. '1 < 2 < 3' parses to '(1 < 2) and (2 < 3)'.
         2. '3 - 4 - 5' parses to '(3 - 4) - 5'.
         3. '3 ^ 4 ^ 5' parses to '3 ^ (4 ^ 5)'.
    

Ruby's convention is clever, but ultimately wrong, and this is one of the
cases.

~~~
xenophanes
1 < 2 < 3 DOES evaluate to (1 < 2) < 3

And 1 < 2 evaluates to true. What else would it evaluate to?

That leaves (true < 3)

Which fails. This isn't a mistake by ruby. When you chain methods you have to
think about what they return.

And the issue here isn't associativity.

~~~
ubernostrum
The comment you're replying to was not asserting that Ruby fails to follow its
own rules, but rather arguing with the rules Ruby follows; other languages
have made an exception to their normal rules for this case, and apparently
some people feel it would be better for Ruby to do the same.

------
gnosis
The real cause of the Ruby problem in the article is the automatic conversion
of "1" to "true". This is a language design bug, and has nothing to do with
associativity.

As for associativity proper, I program in multiple languages all the time, but
find remembering the various associativity rule differences in these languages
a pain in the ass. So I always use parenthesis.

That way anyone reading my code (including myself a few months later) will
find it easy to understand what I intended, and the right thing gets done no
matter what the associativity rules are.

------
enum
Is Python really pulling a syntactic trick?

I think < is simply left associative: True < 4 is True.

Similarly, I think > is right associative. (50 > 30) > 10 does not hold, but
50 > (30 > 10) holds.

~~~
jedi_stannis
Yes it is really pulling a syntactic trick and not comparing against bools.
From the docs:

Comparisons can be chained arbitrarily, e.g., x < y <= z is equivalent to x <
y and y <= z, except that y is evaluated only once.

<http://docs.python.org/reference/expressions.html#notin>

------
brazzy
So what does Perl do with "jfm" + 3 that's so enormously funny?

~~~
nostrademons
Try it:

    
    
        >>> perl -e 'print "jfm" + 3'
        3
    

My guess is that it coerces "jfm" to an int (0), and then adds that to 3 to
get 3.

~~~
jerf
It's a _bit_ of a low blow on the author's part, as in perl, "+" is not "a
generic addition operator that can be overloaded to concatenate strings", it
is literally the "numeric addition operator" and both sides of the + will be
coerced as needed to turn them into numbers. The default for strings is to
parse them as numbers ("10" + "20" yields the number 30), and a string that
doesn't start with a number defaults to zero.

If Perl does anything wrong here, it is in not throwing a type error at
runtime but just silently coercing the number. Compare with Python:

    
    
        Python 2.6.4 ... linux2
        Type "help", ...
        >>> "30" + 2
        Traceback (most recent call last):
          File "<stdin>", line 1, in <module>
        TypeError: cannot concatenate 'str' and 'int' objects
    

(Using "use overload" may let you do further crazy things with +; I'm just
talking about the default definition of the operator.)

Incidentally, the string equivalent of + is ".", the dot. There's a parallel
hierarchy of operators in Perl for numbers vs. strings, which is part of why
Perl has so damned many operators. Woe betide the developer that says "=="
instead of "eq" accidentally....

~~~
sundarurfriend
I actually like the fact that it has different operators for numbers and
strings. It somehow feels cleaner to me, this separation of contexts (contexts
in the usual sense of the word, not strictly Perl contexts).

The fact that the string equivalents are all words (cmp, eq, ne, etc.) gives a
useful mnemonic (strings usually contain 'words', so their operators are
words), and also prevents ugliness from operator-proliferation - it's the
symbol operators that usually make the code _look_ ugly and lead to claims of
'line noise'.

~~~
chromatic
> It somehow feels cleaner to me, this separation of contexts (contexts in the
> usual sense of the word, not strictly Perl contexts).

I've made the argument that the intended _type_ of an operation is as much a
context as the amount (void/scalar/list) context. The relevant Perl 5
literature flirts with that phrasing, but Perl 6 goes much further to extend
that metaphor. That has proven very useful in discussions of consistency.

------
scythe
>You might think a compiler could do several optimizations here, and you'd be
right. But you'd only be right if the compiler could reorder the terms in A +
B + C, or even if it could apply the notion that (A + B) + C == A + (B + C).
But it ain't so in Java, at least not when I last looked at it.

Except Java is statically typed, so it's possible for the compiler to _know_
if f returns an int, and it can rearrange the expression given that knowledge.

~~~
pmjordan
Java throws exceptions on overflow (and underflow), which presumably
interferes with that, e.g.:

    
    
      int A = 3000000000;
      int B = 3000000000;
      int C = -3000000000;
      int D;
      D = A + B + C;
    

Depending on order of evaluation, this either throws an overflow exception or
assigns the number 3 billion to D. There is a hard rule in the spec that
defines what must happen here. In C or C++ there are no such exceptions, so
the compiler can (and does) transform arithmetic expressions where it's
advantageous. (this is obviously a trivial example which will compile down to
a constant anyway)

~~~
prodigal_erik
But
[http://java.sun.com/docs/books/jls/second_edition/html/types...](http://java.sun.com/docs/books/jls/second_edition/html/typesValues.doc.html#29775)
says

> The built-in integer operators do not indicate overflow or underflow in any
> way. The only numeric operators that can throw an exception are the integer
> divide operator / and the integer remainder operator %, which throw an
> ArithmeticException if the right-hand operand is zero.

Maybe it's true that subexpressions can't be reordered for other reasons, but
offhand I can't find that restriction.

