Multiple comparison operators used together are chained, as if connected via the `&&`

operator. This can be useful for readable and mathematically concise comparison chains, such as

```
# same as 0 < i && i <= length(A)
isinbounds(A, i) = 0 < i ≤ length(A)
# same as Set() != x && issubset(x, y)
isnonemptysubset(x, y) = Set() ≠ x ⊆ y
```

However, there is an important difference between `a > b > c`

and `a > b && b > c`

; in the latter, the term `b`

is evaluated twice. This does not matter much for plain old symbols, but could matter if the terms themselves have side effects. For instance,

```
julia> f(x) = (println(x); 2)
f (generic function with 1 method)
julia> 3 > f("test") > 1
test
true
julia> 3 > f("test") && f("test") > 1
test
test
true
```

Let’s take a deeper look at chained comparisons, and how they work, by seeing how they are parsed and lowered into expressions. First, consider the simple comparison, which we can see is just a plain old function call:

```
julia> dump(:(a > b))
Expr
head: Symbol call
args: Array{Any}((3,))
1: Symbol >
2: Symbol a
3: Symbol b
typ: Any
```

Now if we chain the comparison, we notice that the parsing has changed:

```
julia> dump(:(a > b >= c))
Expr
head: Symbol comparison
args: Array{Any}((5,))
1: Symbol a
2: Symbol >
3: Symbol b
4: Symbol >=
5: Symbol c
typ: Any
```

After parsing, the expression is then lowered to its final form:

```
julia> expand(:(a > b >= c))
:(begin
unless a > b goto 3
return b >= c
3:
return false
end)
```

and we note indeed that this is the same as for `a > b && b >= c`

:

```
julia> expand(:(a > b && b >= c))
:(begin
unless a > b goto 3
return b >= c
3:
return false
end)
```