Why are numeric comparisions constrained to support only two args?

question
proposal

#1

Sadly, it is not possible to do:

op(1,2,3,4,5) where op \in {<,>,<=,>=,==,!=}

Why not add support for op to support varargs?

Beside that, why is && and || not a operator as the use of these two symbols as function is not allowed?


#2

Because those will not have the same semantics and ||/&& are control flow syntax and not functions.


#3

What mean not have the same semantics. In lisp (< 1 2 3 4 5) is ok?


#4
  1. This is not lisp
  2. Both of them (chained comparison and ||/&&) has shortcut evaluation semantics.

#5

Can you explain that more. What you mean with shortcut semantics? Did you mean that if the first was false, then the other terms are not evaluated. This can be done in the varargs version <(args…), too.


#6

This can’t be done with a varargs version, because function arguments are evaluated before the function is called. This is why && and || are not function calls, they are special syntactic constructs.

Note that you can do x < y < z < ... and other “chained” comparisons in Julia.


#7

And this is why the

is very (/more) important.


#8

Ok, Ithis is true for && and ||, but why the numeric comparision? Why we allow two args functions for < but not many args as wee need?


#9

Because


#10

Again, what do you mean with shortcut evaluation semantics? Where is the contradiction


#11

I will show you want I want and it works. I must only redefine < with variable args by isless. Here is the code:
function <(x…)
result=true
for index in 1:length(x)-1
result =result && isless(x[index],x[index+1])
end
result
end


#12

I had a vararg comparison PR open here: https://github.com/JuliaLang/julia/pull/22638

It didn’t get much response.


#13

What I mean is exactly what you said. I.e.

< is a function so it works in that sense but it still doesn’t have the same semantics. All the function arguments are evaluated.


#14

Maybe an example will clarify. With chained <, we get lazy evaluation:

julia> f() = (println("f() is evaluated"); 2)
f (generic function with 1 method)

julia> 2 < 1 < f()   # Will not call f()
false

Compare to using @Sighoya’s function from the above post

julia> <(2,1,f())
f() is evaluated
false

Here, f() is called before <, so it is evaluated even though 2 is not less than 1.

To make <(x...) work as expected (without making < into a special case) Julia would have to apply lazy evaluation to function arguments in general. This would be a really cool feature, but probably also really hard to implement and/or impossible to reconcile with dynamic dispatch.


#15

@Per, Thanks, I understand it yet. And to deliver a strict evaluatuion variant with varargs and a lazy evaluation variant with chaining is unidiomatic, right?

For me, it sucks that it cannot be used ala map(op, [1,2,3],[4,5,6],[7,8,9]) with op \in {<,>,<=,>=,==,!=,%unicode variants…%}. But fortunately, I can redefine these symbols, which is sadly not possible with &&, ||.

Did you plan to introduce “or” + “and” to use as alias for || and && in future?. Otherwise, I can write my own “and” and “or” variant accepting varargs.

The support of lazy evaluation would be good. Does julia have generators like in python. For instance that one could write something like that:
<(2,1 yield f())


#16

I think it would be fine, honestly; I would be happy to merge some form of https://github.com/JuliaLang/julia/pull/22638


#17

Try something like

struct Chain{Tf}
    f::Tf
end

(c::Chain)(x, y) = c.f(x, y)

(c::Chain)(x, y, z...) = c(x, y) && c(y, z...)

map(Chain(<), [1,2,3],[4,5,6],[7,8,9])

#18

@stevengj
I’m lucky to see that someone else feels the same pain, thanks for this link

@Tamas_Papp
Interesting… Does it cale for many lists? Because it seems to be a recursion variant.

Is there any reason why ||, && are control flows and not first class functions? Because numeric comparisions support shortcut circuit, too.


#19

It would be a little confusing since it won’t be the same as chained comparison. May not be a big deal assuming no one is going to write chained comparison as function calls…

Chained comparisons supports short circuit evaluation by NOT being functions so for the same reason ||/&& aren’t functions.


#20

@yuyichao
But why support numeric comparision short circuit evaluation when they ARE functions.
I mean 1<0<f() supports short circuit evaluation but < is a function
false && true && g() supports short circuit evaluation, but && is not a function.