A != b != c

proposal

#1

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


#2

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))
:(begin 
        unless a < b goto 3
        return b < c
        3: 
        return false
    end)

It would seems strange to me that other relations should.


#3

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


#4

@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.


Speculation chained comparison optimization (possibly with outlining)
#5

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.


#6

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.


#7

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?


#8

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


#9

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

#10

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
true

julia> 1 ≈ 1 + 2e-8
false

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


#11

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)


#12

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


#13

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!)


#14

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
true

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?


#15

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.


Method definition overwritten warning for functions with default arguments
#16

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


Speculation chained comparison optimization (possibly with outlining)
Speculation chained comparison optimization (possibly with outlining)
Speculation chained comparison optimization (possibly with outlining)
#17

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?


Speculation chained comparison optimization (possibly with outlining)
#18

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.


Speculation chained comparison optimization (possibly with outlining)
#19

You’re now actively spreading misinformation. Please stop.


Speculation chained comparison optimization (possibly with outlining)
#20

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.