Is there any way to make custom binary infix operators right associative?

If I define some new binary infix operator, it appears to always be left associative by default, i.e.

julia> ⨳(a,b) = (a,b)
julia> 1⨳2⨳3
((1,2),3)

Is there any to make it right associative instead? I had guessed defining something like this might work but it doesn’t,

⨳(args...) = foldr(⨳,args)

the call appears to always go to the two-argument function first with assumed left assosciativity.

Associativity is a property of the operator. The only way to do this is to use a right associative binary operator. I believe arrows fit this description, as do assignments.

Yep, you’re right about arrows,

julia> →(a,b) = (a,b)
julia> 1→2→3
(1,(2,3))

Any idea where there’s a list of what Julia assumes for each operator?

I guess it doesn’t make much sense to me that Julia picks the assosciativity based on the unicode character. Shouldn’t I, the programmer defining what these operators mean, get to pick?

In order to define associativity the way you have, operators have to be parsed n-ary. Currently the only n-ary operators are + and * which are usually associative anyway. Unfortunately, even with n-ary parsing, mixing different operators with the same precedence will have the operator’s default associativity.

The reason operator associativity cannot be defined by the programmer in general (except via nary parsing, if applicable) is that the precedence of the operator must be known at parse time, whereas what the operator means is only knowable at runtime in general.

2 Likes

I believe every operator listed as prec-arrow or prec-power in https://github.com/JuliaLang/julia/blob/master/src/julia-parser.scm is right-associative.

4 Likes

Very helpful, explanation, thanks. So yea, it seems that for operators parsed as n-ary my attemped solution above works, e.g.

julia> import Base: +
julia> +(args::String...) = foldr(+,args)
julia> +(a::String, b::String) = "($a,$b)"
julia> "a"+"b"+"c"
"(a,(b,c))"

So I think really what I’d want is more operators to be parsed n-ary like + and * are (e.g. if my ⨳ above was included I’d be good). Which leads me to find https://github.com/JuliaLang/julia/issues/7368 where I’ll probably comment about this particular usefulness of parsing more things as n-ary, which doesn’t seem to have been mentioned there.

This PR updates the documentation to list operator associativity as well as precedence:
https://github.com/JuliaLang/julia/pull/23754

2 Likes

I wish there were more n-ary operators in Julia. There are cases where it’s more efficient to operate on all arguments at once instead of processing one after the other. Is there any hope that the parsing gets changed? For example, an infix operator op could be parsed to something like nary(op, x...) with default method foldl(op, x) or foldr(op, x). That would be similar to Base.literal_pow for powers with constant exponents.