A != b != c

a == b == c is a very nice construct. a < b > c too. however julia allows a != b != c, but i would call it contraintuitive, because it is true if a == c. my thinking is that != should not allow this at all, for it lacks common sense meaning. but if it is allowed, i would suggest translate to a != b && a != c && b != c

Why? Even a < b < c does not assume transitivity, it just happens that it is the case in most use cases.

julia> expand(:(a<b<c))
        unless a < b goto 3
        return b < c
        return false

It would seems strange to me that other relations should.


is there a use case in which < is not transitive?

@pint, I think @Evizero’s point is that < is an arbitrary user-defined function, so there is nothing (except for sanity) stopping you from defining a non-transitive <. The a < b < c implementation does not compare a < c, it only compares a < b and b < c.


but that is an abuse of the operator. the != chain is incorrect even if i don’t abuse the operator, in fact it gives surprising result even when used with numbers.

Here the problem would be the user that wrote the chain of != and expected it to mean something useful.
You wouldn’t write that in mathematics expecting it to be meaningful, and so shouldn’t write it in code either.

What would happen in your example here then?

Or a simpler example: What would a <= b < c translate to, how do I compare a and c. Is there a operator promote or are all possibilities evaluated?

i don’t see how a <= b < c can lead to ambiguity.

I am not sure about even that — below is stylized code for an exercise I sometimes ask students to do, to show how people are not good at generating random numbers:

d1 = [rand(1:3, 3) for i in 1:100]
d2 = # ask people to write down "random" triples containing 1:3
twochanges(v) = v[1] != v[2] != v[3]
sum(twochanges(v) for v in d1) # will be lower
sum(twochanges(v) for v in d2) # people tend to favor sequences with changes
1 Like

Note also that chaining works for arbitrary “comparison” operators, including user-defined operators, and does not require transitivity. For example, consider the operator, which is a synonym for isapprox. This comparison is not transitive, but can be chained:

julia> 1 ≈ 1 + 1e-8 ≈ 1 + 2e-8

julia> 1 ≈ 1 + 2e-8

The behavior for != is consistent with the behavior for other comparison operators: chaining implies pairwise comparisons between adjacent terms only.


I hadn’t even realized that a == b == c was lowered that way, instead of how it would be in C/C++ and C-like languages.
I would have thought that it would mean something totally different, i.e. (a == b) == c, which then return true if c were either Bool or something that converted to true or false and matched the result of a == b.

This may be worth a note in the C/C++ differences section of the manual.
(I’m not saying there is anything wrong with the way Julia handles this, just that it can be surprising, hence a desire for having pointed out in an appropriate part of documentation)

See http://docs.julialang.org/en/stable/manual/mathematical-operations/#chaining-comparisons


Ah, thanks! I also wasn’t trying to imply that it wasn’t well documented somewhere, just that it might be worth having a line in at least the C/C++ differences section (which I was the initial author of ages ago). What do you think of doing that? (and pointing a reference to the chaining comparisons section there also).
I know of course that chaining worked with <, <=, > and >=, just hadn’t realized it worked for arbitrary operators (pretty cool, that!)

I must say that I find the current meaning of a != b != c to be exactly what I intuitively expected, and I was thoroughly confused as to what you even meant at first.

I read it like this: a is different from b and b is different from c. So

julia> 1 != 2 != 1

is exactly as it should be. If you want to test pairwise inequality for all combinations, you would do a != b != c != a.

Inequality is not a transitive property, so why do you expect transitive behavior?


I think you’re getting at the real problem with this kind of discussion: people don’t all have the same intuitions, so debating intuitions doesn’t generally lead to any improvements in language design.


5 posts were merged into an existing topic: Does Julia ever do outlining?

It should be in all cases (as logic dictates), the expand you showed, only shows what the current implementations of Julia does.

In case when you do not have transitive, e.g. a < b > c, or != or the sneaky ≈ (isapprox), then for the optimizer, should split up and optimize the left and right of it separately.

“so there is nothing (except for sanity) stopping you from defining a non-transitive <”

Can that possibility be disallowed so the optimizer does not have to consider that case?

I believe no… but math has a way of surprising me… I don’t know half of it… E.g. I didn’t give this much though until I realized (with more advanced “numbers” they lose more properties):

For complex number (and matrices of anything, except trivial 1x1?) < is not defined. Other than that, I believe when < is defined, it’s always defined the same way. Otherwise, I’m in trouble… and I really need to know.

You’re now actively spreading misinformation. Please stop.

1 Like

A linked list chain where you define a<b to mean a is immediately to the left of b? That sounds like a perfectly useful and valid dispatch for < for which it wouldn’t be transitive. As @stevengj noted, it’s just an operator, so you can do whatever is sensible with it.